diff --git a/ecs/cmd/commands/compose.go b/ecs/cmd/commands/compose.go index fc5802e0b..b3489baee 100644 --- a/ecs/cmd/commands/compose.go +++ b/ecs/cmd/commands/compose.go @@ -7,9 +7,10 @@ import ( "os" "strings" + "github.com/compose-spec/compose-go/cli" + "github.com/compose-spec/compose-go/types" "github.com/docker/cli/cli/command" amazon "github.com/docker/ecs-plugin/pkg/amazon/backend" - "github.com/docker/ecs-plugin/pkg/compose" "github.com/docker/ecs-plugin/pkg/docker" "github.com/spf13/cobra" ) @@ -18,8 +19,8 @@ func ComposeCommand(dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ Use: "compose", } - opts := &compose.ProjectOptions{} - opts.AddFlags(cmd.Flags()) + opts := &cli.ProjectOptions{} + AddFlags(opts, cmd.Flags()) cmd.AddCommand( ConvertCommand(dockerCli, opts), @@ -42,10 +43,10 @@ func (o upOptions) LoadBalancerArn() *string { return &o.loadBalancerArn } -func ConvertCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobra.Command { +func ConvertCommand(dockerCli command.Cli, projectOpts *cli.ProjectOptions) *cobra.Command { cmd := &cobra.Command{ Use: "convert", - RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error { + RunE: WithProject(projectOpts, func(project *types.Project, args []string) error { clusteropts, err := docker.GetAwsContext(dockerCli) if err != nil { return err @@ -71,7 +72,7 @@ func ConvertCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) return cmd } -func UpCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobra.Command { +func UpCommand(dockerCli command.Cli, projectOpts *cli.ProjectOptions) *cobra.Command { opts := upOptions{} cmd := &cobra.Command{ Use: "up", @@ -87,11 +88,11 @@ func UpCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobr return cmd } -func PsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobra.Command { +func PsCommand(dockerCli command.Cli, projectOpts *cli.ProjectOptions) *cobra.Command { opts := upOptions{} cmd := &cobra.Command{ Use: "ps", - RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error { + RunE: WithProject(projectOpts, func(project *types.Project, args []string) error { clusteropts, err := docker.GetAwsContext(dockerCli) if err != nil { return err @@ -120,7 +121,7 @@ type downOptions struct { DeleteCluster bool } -func DownCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobra.Command { +func DownCommand(dockerCli command.Cli, projectOpts *cli.ProjectOptions) *cobra.Command { opts := downOptions{} cmd := &cobra.Command{ Use: "down", @@ -136,7 +137,7 @@ func DownCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co return cmd } -func LogsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobra.Command { +func LogsCommand(dockerCli command.Cli, projectOpts *cli.ProjectOptions) *cobra.Command { cmd := &cobra.Command{ Use: "logs [PROJECT NAME]", RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error { @@ -147,7 +148,7 @@ func LogsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co var name string if len(args) == 0 { - project, err := compose.ProjectFromOptions(projectOpts) + project, err := cli.ProjectFromOptions(projectOpts) if err != nil { return err } diff --git a/ecs/pkg/compose/opts.go b/ecs/cmd/commands/opts.go similarity index 53% rename from ecs/pkg/compose/opts.go rename to ecs/cmd/commands/opts.go index d2fef0c9d..7d0856c86 100644 --- a/ecs/pkg/compose/opts.go +++ b/ecs/cmd/commands/opts.go @@ -1,26 +1,23 @@ -package compose +package commands import ( + "github.com/compose-spec/compose-go/cli" + "github.com/compose-spec/compose-go/types" "github.com/spf13/cobra" "github.com/spf13/pflag" ) -type ProjectOptions struct { - ConfigPaths []string - Name string -} - -func (o *ProjectOptions) AddFlags(flags *pflag.FlagSet) { +func AddFlags(o *cli.ProjectOptions, flags *pflag.FlagSet) { flags.StringArrayVarP(&o.ConfigPaths, "file", "f", nil, "Specify an alternate compose file") flags.StringVarP(&o.Name, "project-name", "n", "", "Specify an alternate project name (default: directory name)") } -type ProjectFunc func(project *Project, args []string) error +type ProjectFunc func(project *types.Project, args []string) error // WithProject wrap a ProjectFunc into a cobra command -func WithProject(options *ProjectOptions, f ProjectFunc) func(cmd *cobra.Command, args []string) error { +func WithProject(options *cli.ProjectOptions, f ProjectFunc) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { - project, err := ProjectFromOptions(options) + project, err := cli.ProjectFromOptions(options) if err != nil { return err } diff --git a/ecs/cmd/commands/secret.go b/ecs/cmd/commands/secret.go index a426740df..b6a32c783 100644 --- a/ecs/cmd/commands/secret.go +++ b/ecs/cmd/commands/secret.go @@ -11,7 +11,7 @@ import ( "github.com/docker/cli/cli/command" amazon "github.com/docker/ecs-plugin/pkg/amazon/backend" - "github.com/docker/ecs-plugin/pkg/amazon/types" + "github.com/docker/ecs-plugin/pkg/compose" "github.com/docker/ecs-plugin/pkg/docker" "github.com/spf13/cobra" ) @@ -57,7 +57,7 @@ func CreateSecret(dockerCli command.Cli) *cobra.Command { } name := args[0] - secret := types.NewSecret(name, opts.Username, opts.Password, opts.Description) + secret := compose.NewSecret(name, opts.Username, opts.Password, opts.Description) id, err := backend.CreateSecret(context.Background(), secret) fmt.Println(id) return err @@ -140,7 +140,7 @@ func DeleteSecret(dockerCli command.Cli) *cobra.Command { return cmd } -func printList(out io.Writer, secrets []types.Secret) { +func printList(out io.Writer, secrets []compose.Secret) { printSection(out, len(secrets), func(w io.Writer) { for _, secret := range secrets { fmt.Fprintf(w, "%s\t%s\t%s\n", secret.ID, secret.Name, secret.Description) diff --git a/ecs/go.mod b/ecs/go.mod index 7dd8d7fe2..438cf8b0c 100644 --- a/ecs/go.mod +++ b/ecs/go.mod @@ -14,7 +14,7 @@ require ( github.com/bugsnag/panicwrap v1.2.0 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cloudflare/cfssl v1.4.1 // indirect - github.com/compose-spec/compose-go v0.0.0-20200409090215-53c0040c9127 + github.com/compose-spec/compose-go v0.0.0-20200622094647-0bb9a6c7d89a github.com/containerd/containerd v1.3.2 // indirect github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb // indirect github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 @@ -35,11 +35,11 @@ require ( github.com/manifoldco/promptui v0.7.0 github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/miekg/pkcs11 v1.0.3 // indirect - github.com/mitchellh/mapstructure v1.2.2 + github.com/mitchellh/mapstructure v1.3.2 github.com/morikuni/aec v1.0.0 // indirect github.com/onsi/ginkgo v1.11.0 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/sirupsen/logrus v1.5.0 + github.com/sirupsen/logrus v1.6.0 github.com/smartystreets/goconvey v1.6.4 // indirect github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 @@ -52,7 +52,6 @@ require ( gopkg.in/fatih/pool.v2 v2.0.0 // indirect gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect gopkg.in/ini.v1 v1.55.0 - gotest.tools v2.2.0+incompatible gotest.tools/v3 v3.0.2 vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787 // indirect ) diff --git a/ecs/go.sum b/ecs/go.sum index f0303a304..9edabc47c 100644 --- a/ecs/go.sum +++ b/ecs/go.sum @@ -54,8 +54,12 @@ 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-20200409090215-53c0040c9127 h1:mAsQN3s19glh3KBOQjiRYBhqaX1SdzNqhB3/cuqgSbE= -github.com/compose-spec/compose-go v0.0.0-20200409090215-53c0040c9127/go.mod h1:1PUpzRF1O/65VOqXZuwpCuYY7pJxbIq1jbAvAf62FGM= +github.com/compose-spec/compose-go v0.0.0-20200616184722-5b8dc203fd7f h1:XE6hHZdPjxN8uGaRlvdCB8YwXbz1PXnQ0CboNygdL2o= +github.com/compose-spec/compose-go v0.0.0-20200616184722-5b8dc203fd7f/go.mod h1:d3Vb4tH01Pr4YKD3RvfwguRcezDBUYJTVYgpCSRYSVg= +github.com/compose-spec/compose-go v0.0.0-20200617133919-fca3bb55c5cc h1:jZfF+HzxW+c8Em308MvcK7j5+ZqIAWqFjN1RZnVFzck= +github.com/compose-spec/compose-go v0.0.0-20200617133919-fca3bb55c5cc/go.mod h1:d3Vb4tH01Pr4YKD3RvfwguRcezDBUYJTVYgpCSRYSVg= +github.com/compose-spec/compose-go v0.0.0-20200622094647-0bb9a6c7d89a h1:FmEuebUePUA0Kd/NSiCmdPG/n6eKdZdBtIbfejVtRS8= +github.com/compose-spec/compose-go v0.0.0-20200622094647-0bb9a6c7d89a/go.mod h1:ih9anT8po+49hrb+1j3ldIJ/YRAaBH52ErlQLTKE2Yo= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -134,9 +138,12 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -151,6 +158,7 @@ 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 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= 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= @@ -183,6 +191,8 @@ github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49/go.mod h1:yyMNCy github.com/kisom/goutils v1.1.0/go.mod h1:+UBTfd78habUYWFbNWTJNG+jNG/i/lGURakr4A/yNRw= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -218,8 +228,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4= -github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -282,8 +292,8 @@ github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvH 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.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= -github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -330,6 +340,7 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2 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 h1:yhqBHs09SmmUoNOHc9jgK4a60T3XFRtPAkYxVnqgY50= 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= @@ -450,6 +461,8 @@ 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= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/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.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= diff --git a/ecs/pkg/amazon/backend/cloudformation.go b/ecs/pkg/amazon/backend/cloudformation.go index 115a67d64..5cdbcdc29 100644 --- a/ecs/pkg/amazon/backend/cloudformation.go +++ b/ecs/pkg/amazon/backend/cloudformation.go @@ -5,14 +5,9 @@ import ( "regexp" "strings" - "github.com/compose-spec/compose-go/types" - - "github.com/sirupsen/logrus" - + ecsapi "github.com/aws/aws-sdk-go/service/ecs" "github.com/aws/aws-sdk-go/service/elbv2" cloudmapapi "github.com/aws/aws-sdk-go/service/servicediscovery" - - 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" @@ -21,10 +16,11 @@ import ( "github.com/awslabs/goformation/v4/cloudformation/logs" cloudmap "github.com/awslabs/goformation/v4/cloudformation/servicediscovery" "github.com/awslabs/goformation/v4/cloudformation/tags" - "github.com/docker/ecs-plugin/pkg/amazon/compatibility" + "github.com/compose-spec/compose-go/compatibility" + "github.com/compose-spec/compose-go/types" sdk "github.com/docker/ecs-plugin/pkg/amazon/sdk" - btypes "github.com/docker/ecs-plugin/pkg/amazon/types" "github.com/docker/ecs-plugin/pkg/compose" + "github.com/sirupsen/logrus" ) const ( @@ -35,11 +31,38 @@ const ( ParameterLoadBalancerARN = "ParameterLoadBalancerARN" ) +type FargateCompatibilityChecker struct { + *compatibility.AllowList +} + // Convert a compose project into a CloudFormation template -func (b Backend) Convert(project *compose.Project) (*cloudformation.Template, error) { - warnings := compatibility.Check(project) - for _, w := range warnings { - logrus.Warn(w) +func (b Backend) Convert(project *types.Project) (*cloudformation.Template, error) { + var checker compatibility.Checker = FargateCompatibilityChecker{ + &compatibility.AllowList{ + Supported: []string{ + "services.command", + "services.container_name", + "services.depends_on", + "services.entrypoint", + "services.environment", + "services.healthcheck", + "services.healthcheck.interval", + "services.healthcheck.start_period", + "services.healthcheck.test", + "services.healthcheck.timeout", + "services.networks", + "services.ports", + "services.ports.mode", + "services.ports.target", + "services.ports.protocol", + "services.user", + "services.working_dir", + }, + }, + } + compatibility.Check(project, checker) + for _, err := range checker.Errors() { + logrus.Warn(err.Error()) } template := cloudformation.NewTemplate() @@ -188,7 +211,7 @@ func (b Backend) Convert(project *compose.Project) (*cloudformation.Template, er return template, nil } -func getLoadBalancerType(project *compose.Project) string { +func getLoadBalancerType(project *types.Project) string { for _, service := range project.Services { for _, port := range service.Ports { if port.Published != 80 && port.Published != 443 { @@ -199,7 +222,7 @@ func getLoadBalancerType(project *compose.Project) string { return elbv2.LoadBalancerTypeEnumApplication } -func getLoadBalancerSecurityGroups(project *compose.Project, template *cloudformation.Template) []string { +func getLoadBalancerSecurityGroups(project *types.Project, template *cloudformation.Template) []string { securityGroups := []string{} for _, network := range project.Networks { if !network.Internal { @@ -210,7 +233,7 @@ func getLoadBalancerSecurityGroups(project *compose.Project, template *cloudform return uniqueStrings(securityGroups) } -func createLoadBalancer(project *compose.Project, template *cloudformation.Template) string { +func createLoadBalancer(project *types.Project, template *cloudformation.Template) string { loadBalancerName := fmt.Sprintf("%sLoadBalancer", strings.Title(project.Name)) // Create LoadBalancer if `ParameterLoadBalancerName` is not set template.Conditions["CreateLoadBalancer"] = cloudformation.Equals("", cloudformation.Ref(ParameterLoadBalancerARN)) @@ -270,7 +293,7 @@ func createListener(service types.ServiceConfig, port types.ServicePortConfig, t return listenerName } -func createTargetGroup(project *compose.Project, service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, protocol string) string { +func createTargetGroup(project *types.Project, service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, protocol string) string { targetGroupName := fmt.Sprintf( "%s%s%dTargetGroup", normalizeResourceName(service.Name), @@ -345,7 +368,7 @@ func createTaskExecutionRole(service types.ServiceConfig, err error, definition return taskExecutionRole, nil } -func createCluster(project *compose.Project, template *cloudformation.Template) string { +func createCluster(project *types.Project, template *cloudformation.Template) string { template.Resources["Cluster"] = &ecs.Cluster{ ClusterName: project.Name, Tags: []tags.Tag{ @@ -360,7 +383,7 @@ func createCluster(project *compose.Project, template *cloudformation.Template) return cluster } -func createCloudMap(project *compose.Project, template *cloudformation.Template) { +func createCloudMap(project *types.Project, template *cloudformation.Template) { template.Resources["CloudMap"] = &cloudmap.PrivateDnsNamespace{ Description: fmt.Sprintf("Service Map for Docker Compose project %s", project.Name), Name: fmt.Sprintf("%s.local", project.Name), @@ -368,8 +391,8 @@ func createCloudMap(project *compose.Project, template *cloudformation.Template) } } -func convertNetwork(project *compose.Project, net types.NetworkConfig, vpc string, template *cloudformation.Template) string { - if sg, ok := net.Extras[btypes.ExtensionSecurityGroup]; ok { +func convertNetwork(project *types.Project, net types.NetworkConfig, vpc string, template *cloudformation.Template) string { + if sg, ok := net.Extensions[compose.ExtensionSecurityGroup]; ok { logrus.Debugf("Security Group for network %q set by user to %q", net.Name, sg) return sg.(string) } @@ -420,7 +443,7 @@ func convertNetwork(project *compose.Project, net types.NetworkConfig, vpc strin return cloudformation.Ref(securityGroup) } -func networkResourceName(project *compose.Project, network string) string { +func networkResourceName(project *types.Project, network string) string { return fmt.Sprintf("%s%sNetwork", normalizeResourceName(project.Name), normalizeResourceName(network)) } diff --git a/ecs/pkg/amazon/backend/cloudformation_test.go b/ecs/pkg/amazon/backend/cloudformation_test.go index 122714781..001931b0a 100644 --- a/ecs/pkg/amazon/backend/cloudformation_test.go +++ b/ecs/pkg/amazon/backend/cloudformation_test.go @@ -7,13 +7,11 @@ import ( "github.com/aws/aws-sdk-go/service/elbv2" "github.com/awslabs/goformation/v4/cloudformation" "github.com/awslabs/goformation/v4/cloudformation/ec2" - "github.com/awslabs/goformation/v4/cloudformation/iam" - "github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2" + "github.com/awslabs/goformation/v4/cloudformation/iam" + "github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" - "github.com/docker/ecs-plugin/pkg/compose" - "gotest.tools/v3/assert" "gotest.tools/v3/golden" ) @@ -58,6 +56,10 @@ version: "3" services: test: image: hello_world + networks: + - front-tier + - back-tier + networks: front-tier: name: public @@ -103,7 +105,7 @@ services: assert.Check(t, lb.Type == elbv2.LoadBalancerTypeEnumNetwork) } -func convertResultAsString(t *testing.T, project *compose.Project, clusterName string) string { +func convertResultAsString(t *testing.T, project *types.Project, clusterName string) string { client, err := NewBackend("", clusterName, "") assert.NilError(t, err) result, err := client.Convert(project) @@ -113,12 +115,12 @@ func convertResultAsString(t *testing.T, project *compose.Project, clusterName s return fmt.Sprintf("%s\n", string(resultAsJSON)) } -func load(t *testing.T, paths ...string) *compose.Project { - options := compose.ProjectOptions{ +func load(t *testing.T, paths ...string) *types.Project { + options := cli.ProjectOptions{ Name: t.Name(), ConfigPaths: paths, } - project, err := compose.ProjectFromOptions(&options) + project, err := cli.ProjectFromOptions(&options) assert.NilError(t, err) return project } @@ -130,14 +132,11 @@ func convertYaml(t *testing.T, yaml string) *cloudformation.Template { ConfigFiles: []types.ConfigFile{ {Config: dict}, }, + }, func(options *loader.Options) { + options.Name = "Test" }) assert.NilError(t, err) - err = compose.Normalize(model) - assert.NilError(t, err) - template, err := Backend{}.Convert(&compose.Project{ - Config: *model, - Name: "test", - }) + template, err := Backend{}.Convert(model) assert.NilError(t, err) return template } diff --git a/ecs/pkg/amazon/backend/down.go b/ecs/pkg/amazon/backend/down.go index adbf9af32..18b4cbf9a 100644 --- a/ecs/pkg/amazon/backend/down.go +++ b/ecs/pkg/amazon/backend/down.go @@ -3,14 +3,14 @@ package backend import ( "context" - "github.com/docker/ecs-plugin/pkg/amazon/types" + "github.com/compose-spec/compose-go/cli" "github.com/docker/ecs-plugin/pkg/compose" ) -func (b *Backend) Down(ctx context.Context, options compose.ProjectOptions) error { +func (b *Backend) Down(ctx context.Context, options cli.ProjectOptions) error { name := options.Name if name == "" { - project, err := compose.ProjectFromOptions(&options) + project, err := cli.ProjectFromOptions(&options) if err != nil { return err } @@ -22,7 +22,7 @@ func (b *Backend) Down(ctx context.Context, options compose.ProjectOptions) erro return err } - err = b.WaitStackCompletion(ctx, name, types.StackDelete) + err = b.WaitStackCompletion(ctx, name, compose.StackDelete) if err != nil { return err } diff --git a/ecs/pkg/amazon/backend/down_test.go b/ecs/pkg/amazon/backend/down_test.go index 7d156e246..7439f626c 100644 --- a/ecs/pkg/amazon/backend/down_test.go +++ b/ecs/pkg/amazon/backend/down_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" + "github.com/compose-spec/compose-go/cli" "github.com/docker/ecs-plugin/pkg/amazon/sdk" - btypes "github.com/docker/ecs-plugin/pkg/amazon/types" "github.com/docker/ecs-plugin/pkg/compose" "github.com/golang/mock/gomock" ) @@ -23,10 +23,10 @@ func TestDown(t *testing.T) { recorder := m.EXPECT() recorder.DeleteStack(ctx, "test_project").Return(nil) recorder.GetStackID(ctx, "test_project").Return("stack-123", nil) - recorder.WaitStackComplete(ctx, "stack-123", btypes.StackDelete).Return(nil) + recorder.WaitStackComplete(ctx, "stack-123", compose.StackDelete).Return(nil) recorder.DescribeStackEvents(ctx, "stack-123").Return(nil, nil) - c.Down(ctx, compose.ProjectOptions{ + c.Down(ctx, cli.ProjectOptions{ ConfigPaths: []string{}, Name: "test_project", }) diff --git a/ecs/pkg/amazon/backend/list.go b/ecs/pkg/amazon/backend/list.go index b90905945..4385eae55 100644 --- a/ecs/pkg/amazon/backend/list.go +++ b/ecs/pkg/amazon/backend/list.go @@ -6,11 +6,11 @@ import ( "sort" "strings" - "github.com/docker/ecs-plugin/pkg/amazon/types" + "github.com/compose-spec/compose-go/types" "github.com/docker/ecs-plugin/pkg/compose" ) -func (b *Backend) Ps(ctx context.Context, project *compose.Project) ([]types.TaskStatus, error) { +func (b *Backend) Ps(ctx context.Context, project *types.Project) ([]compose.TaskStatus, error) { cluster := b.Cluster if cluster == "" { cluster = project.Name @@ -19,17 +19,17 @@ func (b *Backend) Ps(ctx context.Context, project *compose.Project) ([]types.Tas for _, service := range project.Services { tasks, err := b.api.ListTasks(ctx, cluster, service.Name) if err != nil { - return []types.TaskStatus{}, err + return []compose.TaskStatus{}, err } arns = append(arns, tasks...) } if len(arns) == 0 { - return []types.TaskStatus{}, nil + return []compose.TaskStatus{}, nil } tasks, err := b.api.DescribeTasks(ctx, cluster, arns...) if err != nil { - return []types.TaskStatus{}, err + return []compose.TaskStatus{}, err } networkInterfaces := []string{} @@ -40,21 +40,21 @@ func (b *Backend) Ps(ctx context.Context, project *compose.Project) ([]types.Tas } publicIps, err := b.api.GetPublicIPs(ctx, networkInterfaces...) if err != nil { - return []types.TaskStatus{}, err + return []compose.TaskStatus{}, err } sort.Slice(tasks, func(i, j int) bool { return strings.Compare(tasks[i].Service, tasks[j].Service) < 0 }) - for i, t := range tasks { + for i, task := range tasks { ports := []string{} - s, err := project.GetService(t.Service) + s, err := project.GetService(task.Service) if err != nil { - return []types.TaskStatus{}, err + return []compose.TaskStatus{}, err } for _, p := range s.Ports { - ports = append(ports, fmt.Sprintf("%s:%d->%d/%s", publicIps[t.NetworkInterface], p.Published, p.Target, p.Protocol)) + ports = append(ports, fmt.Sprintf("%s:%d->%d/%s", publicIps[task.NetworkInterface], p.Published, p.Target, p.Protocol)) } tasks[i].Name = s.Name tasks[i].Ports = ports diff --git a/ecs/pkg/amazon/backend/secrets.go b/ecs/pkg/amazon/backend/secrets.go index f2ae7c678..6c86e95d8 100644 --- a/ecs/pkg/amazon/backend/secrets.go +++ b/ecs/pkg/amazon/backend/secrets.go @@ -3,18 +3,18 @@ package backend import ( "context" - "github.com/docker/ecs-plugin/pkg/amazon/types" + "github.com/docker/ecs-plugin/pkg/compose" ) -func (b Backend) CreateSecret(ctx context.Context, secret types.Secret) (string, error) { +func (b Backend) CreateSecret(ctx context.Context, secret compose.Secret) (string, error) { return b.api.CreateSecret(ctx, secret) } -func (b Backend) InspectSecret(ctx context.Context, id string) (types.Secret, error) { +func (b Backend) InspectSecret(ctx context.Context, id string) (compose.Secret, error) { return b.api.InspectSecret(ctx, id) } -func (b Backend) ListSecrets(ctx context.Context) ([]types.Secret, error) { +func (b Backend) ListSecrets(ctx context.Context) ([]compose.Secret, error) { return b.api.ListSecrets(ctx) } diff --git a/ecs/pkg/amazon/backend/up.go b/ecs/pkg/amazon/backend/up.go index 8c78748a4..24c75d5c6 100644 --- a/ecs/pkg/amazon/backend/up.go +++ b/ecs/pkg/amazon/backend/up.go @@ -4,12 +4,13 @@ import ( "context" "fmt" - "github.com/docker/ecs-plugin/pkg/amazon/types" + "github.com/compose-spec/compose-go/cli" + "github.com/compose-spec/compose-go/types" "github.com/docker/ecs-plugin/pkg/compose" ) -func (b *Backend) Up(ctx context.Context, options compose.ProjectOptions) error { - project, err := compose.ProjectFromOptions(&options) +func (b *Backend) Up(ctx context.Context, options cli.ProjectOptions) error { + project, err := cli.ProjectFromOptions(&options) if err != nil { return err } @@ -66,12 +67,12 @@ func (b *Backend) Up(ctx context.Context, options compose.ProjectOptions) error } fmt.Println() - return b.WaitStackCompletion(ctx, project.Name, types.StackCreate) + return b.WaitStackCompletion(ctx, project.Name, compose.StackCreate) } -func (b Backend) GetVPC(ctx context.Context, project *compose.Project) (string, error) { +func (b Backend) GetVPC(ctx context.Context, project *types.Project) (string, error) { //check compose file for custom VPC selected - if vpc, ok := project.Extras[types.ExtensionVPC]; ok { + if vpc, ok := project.Extensions[compose.ExtensionVPC]; ok { vpcID := vpc.(string) ok, err := b.api.VpcExists(ctx, vpcID) if err != nil { @@ -88,9 +89,9 @@ func (b Backend) GetVPC(ctx context.Context, project *compose.Project) (string, return defaultVPC, nil } -func (b Backend) GetLoadBalancer(ctx context.Context, project *compose.Project) (string, error) { +func (b Backend) GetLoadBalancer(ctx context.Context, project *types.Project) (string, error) { //check compose file for custom VPC selected - if lb, ok := project.Extras[types.ExtensionLB]; ok { + if lb, ok := project.Extensions[compose.ExtensionLB]; ok { lbName := lb.(string) ok, err := b.api.LoadBalancerExists(ctx, lbName) if err != nil { diff --git a/ecs/pkg/amazon/compatibility/check.go b/ecs/pkg/amazon/compatibility/check.go deleted file mode 100644 index 8e46becba..000000000 --- a/ecs/pkg/amazon/compatibility/check.go +++ /dev/null @@ -1,41 +0,0 @@ -package compatibility - -import ( - "github.com/compose-spec/compose-go/types" - "github.com/docker/ecs-plugin/pkg/compose" -) - -type Warning string -type Warnings []string - -type Checker interface { - CheckService(service *types.ServiceConfig) - CheckCapAdd(service *types.ServiceConfig) - CheckDNS(service *types.ServiceConfig) - CheckDNSOpts(service *types.ServiceConfig) - CheckDNSSearch(service *types.ServiceConfig) - CheckDomainName(service *types.ServiceConfig) - CheckExtraHosts(service *types.ServiceConfig) - CheckHostname(service *types.ServiceConfig) - CheckIpc(service *types.ServiceConfig) - CheckLabels(service *types.ServiceConfig) - CheckLinks(service *types.ServiceConfig) - CheckLogging(service *types.ServiceConfig) - CheckMacAddress(service *types.ServiceConfig) - CheckNetworkMode(service *types.ServiceConfig) - CheckPid(service *types.ServiceConfig) - CheckSysctls(service *types.ServiceConfig) - CheckTmpfs(service *types.ServiceConfig) - CheckUserNSMode(service *types.ServiceConfig) - Errors() []error -} - -// Check the compose model do not use unsupported features and inject sane defaults for ECS deployment -func Check(project *compose.Project) []error { - c := FargateCompatibilityChecker{} - for i, service := range project.Services { - c.CheckService(&service) - project.Services[i] = service - } - return c.errors -} diff --git a/ecs/pkg/amazon/compatibility/check_test.go b/ecs/pkg/amazon/compatibility/check_test.go deleted file mode 100644 index f65898537..000000000 --- a/ecs/pkg/amazon/compatibility/check_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package compatibility - -import ( - "testing" - - "github.com/docker/ecs-plugin/pkg/compose" - "gotest.tools/v3/assert" -) - -func load(t *testing.T, paths ...string) *compose.Project { - options := compose.ProjectOptions{ - Name: t.Name(), - ConfigPaths: paths, - } - project, err := compose.ProjectFromOptions(&options) - assert.NilError(t, err) - return project -} -func TestInvalidNetworkMode(t *testing.T) { - project := load(t, "../backend/testdata/invalid_network_mode.yaml") - err := Check(project) - assert.Error(t, err[0], "'network_mode' \"bridge\" is not supported") -} diff --git a/ecs/pkg/amazon/compatibility/compatibility.go b/ecs/pkg/amazon/compatibility/compatibility.go deleted file mode 100644 index c7d67c26d..000000000 --- a/ecs/pkg/amazon/compatibility/compatibility.go +++ /dev/null @@ -1,171 +0,0 @@ -package compatibility - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/compose-spec/compose-go/types" -) - -type FargateCompatibilityChecker struct { - errors []error -} - -func (c *FargateCompatibilityChecker) error(message string, args ...interface{}) { - c.errors = append(c.errors, fmt.Errorf(message, args...)) -} - -func (c *FargateCompatibilityChecker) Errors() []error { - return c.errors -} - -func (c *FargateCompatibilityChecker) CheckService(service *types.ServiceConfig) { - c.CheckCapAdd(service) - c.CheckDNS(service) - c.CheckDNSOpts(service) - c.CheckDNSSearch(service) - c.CheckDomainName(service) - c.CheckExtraHosts(service) - c.CheckHostname(service) - c.CheckIpc(service) - c.CheckLabels(service) - c.CheckLinks(service) - c.CheckLogging(service) - c.CheckMacAddress(service) - c.CheckNetworkMode(service) - c.CheckPid(service) - c.CheckSysctls(service) - c.CheckTmpfs(service) - c.CheckUserNSMode(service) -} - -func (c *FargateCompatibilityChecker) CheckNetworkMode(service *types.ServiceConfig) { - if service.NetworkMode != "" && service.NetworkMode != ecs.NetworkModeAwsvpc { - c.error("'network_mode' %q is not supported", service.NetworkMode) - } - service.NetworkMode = ecs.NetworkModeAwsvpc -} - -func (c *FargateCompatibilityChecker) CheckLinks(service *types.ServiceConfig) { - if len(service.Links) != 0 { - c.error("'links' is not supported") - service.Links = nil - } -} - -func (c *FargateCompatibilityChecker) CheckLogging(service *types.ServiceConfig) { - c.CheckLoggingDriver(service) -} - -func (c *FargateCompatibilityChecker) CheckLoggingDriver(service *types.ServiceConfig) { - if service.LogDriver != "" && service.LogDriver != ecs.LogDriverAwslogs { - c.error("'log_driver' %q is not supported", service.LogDriver) - service.LogDriver = ecs.LogDriverAwslogs - } -} - -func (c *FargateCompatibilityChecker) CheckPid(service *types.ServiceConfig) { - if service.Pid != "" { - c.error("'pid' is not supported") - service.Pid = "" - } -} - -func (c *FargateCompatibilityChecker) CheckUserNSMode(service *types.ServiceConfig) { - if service.UserNSMode != "" { - c.error("'userns_mode' is not supported") - service.UserNSMode = "" - } -} - -func (c *FargateCompatibilityChecker) CheckIpc(service *types.ServiceConfig) { - if service.Ipc != "" { - c.error("'ipc' is not supported") - service.Ipc = "" - } -} - -func (c *FargateCompatibilityChecker) CheckMacAddress(service *types.ServiceConfig) { - if service.MacAddress != "" { - c.error("'mac_address' is not supported") - service.MacAddress = "" - } -} - -func (c *FargateCompatibilityChecker) CheckHostname(service *types.ServiceConfig) { - if service.Hostname != "" { - c.error("'hostname' is not supported") - service.Hostname = "" - } -} - -func (c *FargateCompatibilityChecker) CheckDomainName(service *types.ServiceConfig) { - if service.DomainName != "" { - c.error("'domainname' is not supported") - service.DomainName = "" - } -} - -func (c *FargateCompatibilityChecker) CheckDNSSearch(service *types.ServiceConfig) { - if len(service.DNSSearch) > 0 { - c.error("'dns_search' is not supported") - service.DNSSearch = nil - } -} - -func (c *FargateCompatibilityChecker) CheckDNS(service *types.ServiceConfig) { - if len(service.DNS) > 0 { - c.error("'dns' is not supported") - service.DNS = nil - } -} - -func (c *FargateCompatibilityChecker) CheckDNSOpts(service *types.ServiceConfig) { - if len(service.DNSOpts) > 0 { - c.error("'dns_opt' is not supported") - service.DNSOpts = nil - } -} - -func (c *FargateCompatibilityChecker) CheckExtraHosts(service *types.ServiceConfig) { - if len(service.ExtraHosts) > 0 { - c.error("'extra_hosts' is not supported") - service.ExtraHosts = nil - } -} - -func (c *FargateCompatibilityChecker) CheckCapAdd(service *types.ServiceConfig) { - for i, v := range service.CapAdd { - if v != "SYS_PTRACE" { - c.error("'cap_add' %s is not supported", v) - l := len(service.CapAdd) - service.CapAdd[i] = service.CapAdd[l-1] - service.CapAdd = service.CapAdd[:l-1] - } - } -} - -func (c *FargateCompatibilityChecker) CheckTmpfs(service *types.ServiceConfig) { - if len(service.Tmpfs) > 0 { - c.error("'tmpfs' is not supported") - service.Tmpfs = nil - } -} - -func (c *FargateCompatibilityChecker) CheckSysctls(service *types.ServiceConfig) { - if len(service.Sysctls) > 0 { - c.error("'sysctls' is not supported") - service.Sysctls = nil - } -} - -func (c *FargateCompatibilityChecker) CheckLabels(service *types.ServiceConfig) { - for k, v := range service.Labels { - if v == "" { - c.error("'labels' with an empty value is not supported") - delete(service.Labels, k) - } - } -} - -var _ Checker = &FargateCompatibilityChecker{} diff --git a/ecs/pkg/amazon/sdk/api.go b/ecs/pkg/amazon/sdk/api.go index 137a43397..1e3e61ad3 100644 --- a/ecs/pkg/amazon/sdk/api.go +++ b/ecs/pkg/amazon/sdk/api.go @@ -5,7 +5,7 @@ import ( cf "github.com/aws/aws-sdk-go/service/cloudformation" "github.com/awslabs/goformation/v4/cloudformation" - "github.com/docker/ecs-plugin/pkg/amazon/types" + "github.com/docker/ecs-plugin/pkg/compose" ) //go:generate mockgen -destination=./api_mock.go -self_package "github.com/docker/ecs-plugin/pkg/amazon" -package=amazon . API @@ -38,19 +38,19 @@ type downAPI interface { } type logsAPI interface { - GetLogs(ctx context.Context, name string, consumer types.LogConsumer) error + GetLogs(ctx context.Context, name string, consumer compose.LogConsumer) error } type secretsAPI interface { - CreateSecret(ctx context.Context, secret types.Secret) (string, error) - InspectSecret(ctx context.Context, id string) (types.Secret, error) - ListSecrets(ctx context.Context) ([]types.Secret, error) + CreateSecret(ctx context.Context, secret compose.Secret) (string, error) + InspectSecret(ctx context.Context, id string) (compose.Secret, error) + ListSecrets(ctx context.Context) ([]compose.Secret, error) DeleteSecret(ctx context.Context, id string, recover bool) error } type listAPI interface { ListTasks(ctx context.Context, cluster string, name string) ([]string, error) - DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]types.TaskStatus, error) + DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]compose.TaskStatus, error) GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error) } diff --git a/ecs/pkg/amazon/sdk/api_mock.go b/ecs/pkg/amazon/sdk/api_mock.go index 07ce79e74..0fe42fb2f 100644 --- a/ecs/pkg/amazon/sdk/api_mock.go +++ b/ecs/pkg/amazon/sdk/api_mock.go @@ -8,9 +8,10 @@ import ( context "context" reflect "reflect" + "github.com/docker/ecs-plugin/pkg/compose" + cloudformation "github.com/aws/aws-sdk-go/service/cloudformation" cloudformation0 "github.com/awslabs/goformation/v4/cloudformation" - btypes "github.com/docker/ecs-plugin/pkg/amazon/types" gomock "github.com/golang/mock/gomock" ) @@ -53,7 +54,7 @@ func (mr *MockAPIMockRecorder) ClusterExists(arg0, arg1 interface{}) *gomock.Cal } // CreateSecret mocks base method -func (m *MockAPI) CreateSecret(arg0 context.Context, arg1 btypes.Secret) (string, error) { +func (m *MockAPI) CreateSecret(arg0 context.Context, arg1 compose.Secret) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateSecret", arg0, arg1) ret0, _ := ret[0].(string) @@ -139,14 +140,14 @@ func (mr *MockAPIMockRecorder) DescribeStackEvents(arg0, arg1 interface{}) *gomo } // DescribeTasks mocks base method -func (m *MockAPI) DescribeTasks(arg0 context.Context, arg1 string, arg2 ...string) ([]btypes.TaskStatus, error) { +func (m *MockAPI) DescribeTasks(arg0 context.Context, arg1 string, arg2 ...string) ([]compose.TaskStatus, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeTasks", varargs...) - ret0, _ := ret[0].([]btypes.TaskStatus) + ret0, _ := ret[0].([]compose.TaskStatus) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -174,7 +175,7 @@ func (mr *MockAPIMockRecorder) GetDefaultVPC(arg0 interface{}) *gomock.Call { } // GetLogs mocks base method -func (m *MockAPI) GetLogs(arg0 context.Context, arg1 string, arg2 btypes.LogConsumer) error { +func (m *MockAPI) GetLogs(arg0 context.Context, arg1 string, arg2 compose.LogConsumer) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLogs", arg0, arg1, arg2) ret0, _ := ret[0].(error) @@ -238,10 +239,10 @@ func (mr *MockAPIMockRecorder) GetSubNets(arg0, arg1 interface{}) *gomock.Call { } // InspectSecret mocks base method -func (m *MockAPI) InspectSecret(arg0 context.Context, arg1 string) (btypes.Secret, error) { +func (m *MockAPI) InspectSecret(arg0 context.Context, arg1 string) (compose.Secret, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InspectSecret", arg0, arg1) - ret0, _ := ret[0].(btypes.Secret) + ret0, _ := ret[0].(compose.Secret) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -253,10 +254,10 @@ func (mr *MockAPIMockRecorder) InspectSecret(arg0, arg1 interface{}) *gomock.Cal } // ListSecrets mocks base method -func (m *MockAPI) ListSecrets(arg0 context.Context) ([]btypes.Secret, error) { +func (m *MockAPI) ListSecrets(arg0 context.Context) ([]compose.Secret, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListSecrets", arg0) - ret0, _ := ret[0].([]btypes.Secret) + ret0, _ := ret[0].([]compose.Secret) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/ecs/pkg/amazon/sdk/convert.go b/ecs/pkg/amazon/sdk/convert.go index 8874aa83e..3e2b861f8 100644 --- a/ecs/pkg/amazon/sdk/convert.go +++ b/ecs/pkg/amazon/sdk/convert.go @@ -13,11 +13,10 @@ import ( "github.com/awslabs/goformation/v4/cloudformation/tags" "github.com/compose-spec/compose-go/types" "github.com/docker/cli/opts" - t "github.com/docker/ecs-plugin/pkg/amazon/types" "github.com/docker/ecs-plugin/pkg/compose" ) -func Convert(project *compose.Project, service types.ServiceConfig) (*ecs.TaskDefinition, error) { +func Convert(project *types.Project, service types.ServiceConfig) (*ecs.TaskDefinition, error) { cpu, mem, err := toLimits(service) if err != nil { return nil, err @@ -318,8 +317,8 @@ func getImage(image string) string { func getRepoCredentials(service types.ServiceConfig) *ecs.TaskDefinition_RepositoryCredentials { // extract registry and namespace string from image name - for key, value := range service.Extras { - if key == t.ExtensionPullCredentials { + for key, value := range service.Extensions { + if key == compose.ExtensionPullCredentials { return &ecs.TaskDefinition_RepositoryCredentials{CredentialsParameter: value.(string)} } } diff --git a/ecs/pkg/amazon/sdk/sdk.go b/ecs/pkg/amazon/sdk/sdk.go index 1e1cb8496..3f0ed9297 100644 --- a/ecs/pkg/amazon/sdk/sdk.go +++ b/ecs/pkg/amazon/sdk/sdk.go @@ -23,10 +23,8 @@ import ( "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" cf "github.com/awslabs/goformation/v4/cloudformation" + "github.com/docker/ecs-plugin/pkg/compose" "github.com/sirupsen/logrus" - - "github.com/docker/ecs-plugin/pkg/amazon/types" - t "github.com/docker/ecs-plugin/pkg/amazon/types" ) type sdk struct { @@ -189,9 +187,9 @@ func (s sdk) WaitStackComplete(ctx context.Context, name string, operation int) StackName: aws.String(name), } switch operation { - case t.StackCreate: + case compose.StackCreate: return s.CF.WaitUntilStackCreateCompleteWithContext(ctx, input) - case t.StackDelete: + case compose.StackDelete: return s.CF.WaitUntilStackDeleteCompleteWithContext(ctx, input) default: return fmt.Errorf("internal error: unexpected stack operation %d", operation) @@ -236,7 +234,7 @@ func (s sdk) DeleteStack(ctx context.Context, name string) error { return err } -func (s sdk) CreateSecret(ctx context.Context, secret t.Secret) (string, error) { +func (s sdk) CreateSecret(ctx context.Context, secret compose.Secret) (string, error) { logrus.Debug("Create secret " + secret.Name) secretStr, err := secret.GetCredString() if err != nil { @@ -254,17 +252,17 @@ func (s sdk) CreateSecret(ctx context.Context, secret t.Secret) (string, error) return *response.ARN, nil } -func (s sdk) InspectSecret(ctx context.Context, id string) (t.Secret, error) { +func (s sdk) InspectSecret(ctx context.Context, id string) (compose.Secret, error) { logrus.Debug("Inspect secret " + id) response, err := s.SM.DescribeSecret(&secretsmanager.DescribeSecretInput{SecretId: &id}) if err != nil { - return t.Secret{}, err + return compose.Secret{}, err } labels := map[string]string{} for _, tag := range response.Tags { labels[*tag.Key] = *tag.Value } - secret := t.Secret{ + secret := compose.Secret{ ID: *response.ARN, Name: *response.Name, Labels: labels, @@ -275,14 +273,14 @@ func (s sdk) InspectSecret(ctx context.Context, id string) (t.Secret, error) { return secret, nil } -func (s sdk) ListSecrets(ctx context.Context) ([]t.Secret, error) { +func (s sdk) ListSecrets(ctx context.Context) ([]compose.Secret, error) { logrus.Debug("List secrets ...") response, err := s.SM.ListSecrets(&secretsmanager.ListSecretsInput{}) if err != nil { - return []t.Secret{}, err + return []compose.Secret{}, err } - var secrets []t.Secret + var secrets []compose.Secret for _, sec := range response.SecretList { @@ -294,7 +292,7 @@ func (s sdk) ListSecrets(ctx context.Context) ([]t.Secret, error) { if sec.Description != nil { description = *sec.Description } - secrets = append(secrets, t.Secret{ + secrets = append(secrets, compose.Secret{ ID: *sec.ARN, Name: *sec.Name, Labels: labels, @@ -311,7 +309,7 @@ func (s sdk) DeleteSecret(ctx context.Context, id string, recover bool) error { return err } -func (s sdk) GetLogs(ctx context.Context, name string, consumer types.LogConsumer) error { +func (s sdk) GetLogs(ctx context.Context, name string, consumer compose.LogConsumer) error { logGroup := fmt.Sprintf("/docker-compose/%s", name) var startTime int64 for { @@ -357,7 +355,7 @@ func (s sdk) ListTasks(ctx context.Context, cluster string, service string) ([]s return arns, nil } -func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]t.TaskStatus, error) { +func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]compose.TaskStatus, error) { tasks, err := s.ECS.DescribeTasksWithContext(ctx, &ecs.DescribeTasksInput{ Cluster: aws.String(cluster), Tasks: aws.StringSlice(arns), @@ -365,7 +363,7 @@ func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string) if err != nil { return nil, err } - result := []t.TaskStatus{} + result := []compose.TaskStatus{} for _, task := range tasks.Tasks { var networkInterface string for _, attachement := range task.Attachments { @@ -377,7 +375,7 @@ func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string) } } } - result = append(result, t.TaskStatus{ + result = append(result, compose.TaskStatus{ State: *task.LastStatus, Service: strings.Replace(*task.Group, "service:", "", 1), NetworkInterface: networkInterface, diff --git a/ecs/pkg/compose/api.go b/ecs/pkg/compose/api.go index 6d84ccec4..0d4877a8b 100644 --- a/ecs/pkg/compose/api.go +++ b/ecs/pkg/compose/api.go @@ -4,19 +4,20 @@ import ( "context" "github.com/awslabs/goformation/v4/cloudformation" - "github.com/docker/ecs-plugin/pkg/amazon/types" + "github.com/compose-spec/compose-go/cli" + "github.com/compose-spec/compose-go/types" ) type API interface { - Up(ctx context.Context, options ProjectOptions) error - Down(ctx context.Context, options ProjectOptions) error + Up(ctx context.Context, options cli.ProjectOptions) error + Down(ctx context.Context, options cli.ProjectOptions) error - Convert(project *Project) (*cloudformation.Template, error) + Convert(project *types.Project) (*cloudformation.Template, error) Logs(ctx context.Context, projectName string) error - Ps(background context.Context, project *Project) ([]types.TaskStatus, error) + Ps(background context.Context, project *types.Project) ([]TaskStatus, error) - CreateSecret(ctx context.Context, secret types.Secret) (string, error) - InspectSecret(ctx context.Context, id string) (types.Secret, error) - ListSecrets(ctx context.Context) ([]types.Secret, error) + CreateSecret(ctx context.Context, secret Secret) (string, error) + InspectSecret(ctx context.Context, id string) (Secret, error) + ListSecrets(ctx context.Context) ([]Secret, error) DeleteSecret(ctx context.Context, id string, recover bool) error } diff --git a/ecs/pkg/compose/normalize.go b/ecs/pkg/compose/normalize.go deleted file mode 100644 index 3e4809d1e..000000000 --- a/ecs/pkg/compose/normalize.go +++ /dev/null @@ -1,89 +0,0 @@ -package compose - -import ( - "fmt" - - "github.com/compose-spec/compose-go/types" - "github.com/sirupsen/logrus" -) - -// Normalize a compose-go model to move deprecated attributes to canonical position, and introduce implicit defaults -// FIXME move this to compose-go -func Normalize(model *types.Config) error { - if len(model.Networks) == 0 { - // Compose application model implies a default network if none is explicitly set. - model.Networks["default"] = types.NetworkConfig{ - Name: "default", - } - } - - for i, s := range model.Services { - if len(s.Networks) == 0 { - // Service without explicit network attachment are implicitly exposed on default network - s.Networks = map[string]*types.ServiceNetworkConfig{"default": nil} - } - - for i, p := range s.Ports { - if p.Published == 0 { - p.Published = p.Target - s.Ports[i] = p - } - } - - if s.LogDriver != "" { - logrus.Warn("`log_driver` is deprecated. Use the `logging` attribute") - if s.Logging == nil { - s.Logging = &types.LoggingConfig{} - } - if s.Logging.Driver == "" { - s.Logging.Driver = s.LogDriver - } else { - return fmt.Errorf("can't use both 'log_driver' (deprecated) and 'logging.driver'") - } - } - if len(s.LogOpt) != 0 { - logrus.Warn("`log_opts` is deprecated. Use the `logging` attribute") - if s.Logging == nil { - s.Logging = &types.LoggingConfig{} - } - for k, v := range s.LogOpt { - if _, ok := s.Logging.Options[k]; !ok { - s.Logging.Options[k] = v - } else { - return fmt.Errorf("can't use both 'log_opt' (deprecated) and 'logging.options'") - } - } - } - model.Services[i] = s - } - - for i, n := range model.Networks { - if n.Name == "" { - n.Name = i - model.Networks[i] = n - } - } - - for i, v := range model.Volumes { - if v.Name == "" { - v.Name = i - model.Volumes[i] = v - } - } - - for i, c := range model.Configs { - if c.Name == "" { - c.Name = i - model.Configs[i] = c - } - } - - for i, s := range model.Secrets { - if s.Name == "" { - s.Name = i - model.Secrets[i] = s - } - } - - return nil -} diff --git a/ecs/pkg/compose/project.go b/ecs/pkg/compose/project.go deleted file mode 100644 index a90bba071..000000000 --- a/ecs/pkg/compose/project.go +++ /dev/null @@ -1,170 +0,0 @@ -package compose - -import ( - "fmt" - "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 { - types.Config - projectDir string - Name string `yaml:"-" json:"-"` -} - -func NewProject(config types.ConfigDetails, name string) (*Project, error) { - model, err := loader.Load(config) - if err != nil { - return nil, err - } - - err = Normalize(model) - if err != nil { - return nil, err - } - - p := Project{ - Config: *model, - projectDir: config.WorkingDir, - Name: name, - } - return &p, nil -} - -// projectFromOptions load a compose project based on command line options -func ProjectFromOptions(options *ProjectOptions) (*Project, error) { - configPath, err := getConfigPathFromOptions(options) - if err != nil { - return nil, err - } - - name := options.Name - if name == "" { - name = os.Getenv("COMPOSE_PROJECT_NAME") - } - - workingDir := filepath.Dir(configPath[0]) - - if name == "" { - r := regexp.MustCompile(`[^a-z0-9\\-_]+`) - name = r.ReplaceAllString(strings.ToLower(filepath.Base(workingDir)), "") - } - - configs, err := parseConfigs(configPath) - if err != nil { - return nil, err - } - - return NewProject(types.ConfigDetails{ - WorkingDir: workingDir, - ConfigFiles: configs, - Environment: environment(), - }, name) -} - -func getConfigPathFromOptions(options *ProjectOptions) ([]string, error) { - paths := []string{} - pwd, err := os.Getwd() - if err != nil { - return nil, err - } - - if len(options.ConfigPaths) != 0 { - for _, f := range options.ConfigPaths { - if f == "-" { - paths = append(paths, f) - continue - } - if !filepath.IsAbs(f) { - f = filepath.Join(pwd, f) - } - if _, err := os.Stat(f); err != nil { - return nil, err - } - paths = append(paths, f) - } - return paths, nil - } - - sep := os.Getenv("COMPOSE_FILE_SEPARATOR") - if sep == "" { - sep = string(os.PathListSeparator) - } - f := os.Getenv("COMPOSE_FILE") - if f != "" { - return strings.Split(f, sep), nil - } - - for { - candidates := []string{} - for _, n := range SupportedFilenames { - f := filepath.Join(pwd, n) - if _, err := os.Stat(f); err == nil { - candidates = append(candidates, f) - } - } - if len(candidates) > 0 { - winner := candidates[0] - if len(candidates) > 1 { - logrus.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", ")) - logrus.Warnf("Using %s\n", winner) - } - return []string{winner}, nil - } - parent := filepath.Dir(pwd) - if parent == pwd { - return nil, fmt.Errorf("Can't find a suitable configuration file in this directory or any parent. Are you in the right directory?") - } - pwd = parent - } -} - -var SupportedFilenames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"} - -func parseConfigs(configPaths []string) ([]types.ConfigFile, error) { - files := []types.ConfigFile{} - for _, f := range configPaths { - var ( - b []byte - err error - ) - if f == "-" { - b, err = ioutil.ReadAll(os.Stdin) - } else { - if _, err := os.Stat(f); err != nil { - return nil, err - } - b, err = ioutil.ReadFile(f) - } - if err != nil { - return nil, err - } - config, err := loader.ParseYAML(b) - if err != nil { - return nil, err - } - files = append(files, types.ConfigFile{Filename: f, Config: config}) - } - return files, nil -} - -func environment() map[string]string { - return getAsEqualsMap(os.Environ()) -} - -// getAsEqualsMap split key=value formatted strings into a key : value map -func getAsEqualsMap(em []string) map[string]string { - m := make(map[string]string) - for _, v := range em { - kv := strings.SplitN(v, "=", 2) - m[kv[0]] = kv[1] - } - return m -} diff --git a/ecs/pkg/compose/project_test.go b/ecs/pkg/compose/project_test.go deleted file mode 100644 index 733f34f03..000000000 --- a/ecs/pkg/compose/project_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package compose - -import ( - "os" - "testing" - - "gotest.tools/v3/assert" -) - -func Test_project_name(t *testing.T) { - p, err := ProjectFromOptions(&ProjectOptions{ - Name: "my_project", - ConfigPaths: []string{"testdata/simple/compose.yaml"}, - }) - assert.NilError(t, err) - assert.Equal(t, p.Name, "my_project") - - p, err = ProjectFromOptions(&ProjectOptions{ - Name: "", - ConfigPaths: []string{"testdata/simple/compose.yaml"}, - }) - assert.NilError(t, err) - assert.Equal(t, p.Name, "simple") - - os.Setenv("COMPOSE_PROJECT_NAME", "my_project_from_env") - p, err = ProjectFromOptions(&ProjectOptions{ - Name: "", - ConfigPaths: []string{"testdata/simple/compose.yaml"}, - }) - assert.NilError(t, err) - assert.Equal(t, p.Name, "my_project_from_env") -} - -func Test_project_from_set_of_files(t *testing.T) { - p, err := ProjectFromOptions(&ProjectOptions{ - Name: "my_project", - ConfigPaths: []string{ - "testdata/simple/compose.yaml", - "testdata/simple/compose-with-overrides.yaml", - }, - }) - assert.NilError(t, err) - service, err := p.GetService("simple") - assert.NilError(t, err) - assert.Equal(t, service.Image, "haproxy") -} diff --git a/ecs/pkg/amazon/types/types.go b/ecs/pkg/compose/types.go similarity index 98% rename from ecs/pkg/amazon/types/types.go rename to ecs/pkg/compose/types.go index f6815955d..133263658 100644 --- a/ecs/pkg/amazon/types/types.go +++ b/ecs/pkg/compose/types.go @@ -1,4 +1,4 @@ -package types +package compose import "encoding/json" diff --git a/ecs/pkg/amazon/types/x.go b/ecs/pkg/compose/x.go similarity index 92% rename from ecs/pkg/amazon/types/x.go rename to ecs/pkg/compose/x.go index d8b8e0112..7a2a5bd0b 100644 --- a/ecs/pkg/amazon/types/x.go +++ b/ecs/pkg/compose/x.go @@ -1,4 +1,4 @@ -package types +package compose const ( ExtensionSecurityGroup = "x-aws-securitygroup"