Sfoglia il codice sorgente

code restructure

Signed-off-by: aiordache <[email protected]>
Signed-off-by: Nicolas De Loof <[email protected]>
aiordache 5 anni fa
parent
commit
bb98dae082
35 ha cambiato i file con 464 aggiunte e 421 eliminazioni
  1. 24 12
      ecs/cmd/commands/compose.go
  2. 12 11
      ecs/cmd/commands/secret.go
  3. 8 0
      ecs/pkg/amazon/amazon.go
  4. 0 11
      ecs/pkg/amazon/api.go
  5. 7 9
      ecs/pkg/amazon/backend/backend.go
  6. 30 26
      ecs/pkg/amazon/backend/cloudformation.go
  7. 4 3
      ecs/pkg/amazon/backend/cloudformation_test.go
  8. 31 0
      ecs/pkg/amazon/backend/down.go
  9. 9 7
      ecs/pkg/amazon/backend/down_test.go
  10. 1 1
      ecs/pkg/amazon/backend/iam.go
  11. 63 0
      ecs/pkg/amazon/backend/list.go
  12. 6 14
      ecs/pkg/amazon/backend/logs.go
  13. 23 0
      ecs/pkg/amazon/backend/secrets.go
  14. 0 0
      ecs/pkg/amazon/backend/testdata/input/simple-single-service-with-overrides.yaml
  15. 0 0
      ecs/pkg/amazon/backend/testdata/input/simple-single-service.yaml
  16. 0 0
      ecs/pkg/amazon/backend/testdata/invalid_network_mode.yaml
  17. 0 0
      ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-conversion.golden
  18. 0 0
      ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-with-overrides-conversion.golden
  19. 100 0
      ecs/pkg/amazon/backend/up.go
  20. 5 17
      ecs/pkg/amazon/backend/wait.go
  21. 0 13
      ecs/pkg/amazon/check_test.go
  22. 1 1
      ecs/pkg/amazon/compatibility/check.go
  23. 23 0
      ecs/pkg/amazon/compatibility/check_test.go
  24. 1 1
      ecs/pkg/amazon/compatibility/compatibility.go
  25. 0 34
      ecs/pkg/amazon/down.go
  26. 0 80
      ecs/pkg/amazon/list.go
  27. 61 0
      ecs/pkg/amazon/sdk/api.go
  28. 10 10
      ecs/pkg/amazon/sdk/api_mock.go
  29. 3 2
      ecs/pkg/amazon/sdk/convert.go
  30. 17 16
      ecs/pkg/amazon/sdk/sdk.go
  31. 0 30
      ecs/pkg/amazon/secrets.go
  32. 19 3
      ecs/pkg/amazon/types/types.go
  33. 1 1
      ecs/pkg/amazon/types/x.go
  34. 0 114
      ecs/pkg/amazon/up.go
  35. 5 5
      ecs/pkg/compose/api.go

+ 24 - 12
ecs/cmd/commands/compose.go

@@ -3,9 +3,12 @@ package commands
 import (
 	"context"
 	"fmt"
+	"io"
+	"os"
+	"strings"
 
 	"github.com/docker/cli/cli/command"
-	"github.com/docker/ecs-plugin/pkg/amazon"
+	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"
@@ -47,11 +50,11 @@ func ConvertCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions)
 			if err != nil {
 				return err
 			}
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
-			template, err := client.Convert(project)
+			template, err := backend.Convert(project)
 			if err != nil {
 				return err
 			}
@@ -77,11 +80,11 @@ func UpCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobr
 			if err != nil {
 				return err
 			}
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
-			return client.ComposeUp(context.Background(), project)
+			return backend.ComposeUp(context.Background(), project)
 		}),
 	}
 	cmd.Flags().StringVar(&opts.loadBalancerArn, "load-balancer", "", "")
@@ -97,11 +100,20 @@ func PsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobr
 			if err != nil {
 				return err
 			}
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
-			return client.ComposePs(context.Background(), project)
+			tasks, err := backend.ComposePs(context.Background(), project)
+			if err != nil {
+				return err
+			}
+			printSection(os.Stdout, len(tasks), func(w io.Writer) {
+				for _, task := range tasks {
+					fmt.Fprintf(w, "%s\t%s\t%s\n", task.Name, task.State, strings.Join(task.Ports, " "))
+				}
+			}, "NAME", "STATE", "PORTS")
+			return nil
 		}),
 	}
 	cmd.Flags().StringVar(&opts.loadBalancerArn, "load-balancer", "", "")
@@ -117,7 +129,7 @@ func DownCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co
 	cmd := &cobra.Command{
 		Use: "down",
 		RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
@@ -126,11 +138,11 @@ func DownCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co
 				if err != nil {
 					return err
 				}
-				return client.ComposeDown(context.Background(), project.Name, opts.DeleteCluster)
+				return backend.ComposeDown(context.Background(), project.Name, opts.DeleteCluster)
 			}
 			// project names passed as parameters
 			for _, name := range args {
-				err := client.ComposeDown(context.Background(), name, opts.DeleteCluster)
+				err := backend.ComposeDown(context.Background(), name, opts.DeleteCluster)
 				if err != nil {
 					return err
 				}
@@ -146,7 +158,7 @@ func LogsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co
 	cmd := &cobra.Command{
 		Use: "logs [PROJECT NAME]",
 		RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
@@ -161,7 +173,7 @@ func LogsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co
 			} else {
 				name = args[0]
 			}
-			return client.ComposeLogs(context.Background(), name)
+			return backend.ComposeLogs(context.Background(), name)
 		}),
 	}
 	return cmd

+ 12 - 11
ecs/cmd/commands/secret.go

@@ -10,7 +10,8 @@ import (
 	"text/tabwriter"
 
 	"github.com/docker/cli/cli/command"
-	"github.com/docker/ecs-plugin/pkg/amazon"
+	amazon "github.com/docker/ecs-plugin/pkg/amazon/backend"
+	"github.com/docker/ecs-plugin/pkg/amazon/types"
 	"github.com/docker/ecs-plugin/pkg/docker"
 	"github.com/spf13/cobra"
 )
@@ -47,7 +48,7 @@ func CreateSecret(dockerCli command.Cli) *cobra.Command {
 		Use:   "create NAME",
 		Short: "Creates a secret.",
 		RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
@@ -56,8 +57,8 @@ func CreateSecret(dockerCli command.Cli) *cobra.Command {
 			}
 			name := args[0]
 
-			secret := docker.NewSecret(name, opts.Username, opts.Password, opts.Description)
-			id, err := client.CreateSecret(context.Background(), secret)
+			secret := types.NewSecret(name, opts.Username, opts.Password, opts.Description)
+			id, err := backend.CreateSecret(context.Background(), secret)
 			fmt.Println(id)
 			return err
 		}),
@@ -73,7 +74,7 @@ func InspectSecret(dockerCli command.Cli) *cobra.Command {
 		Use:   "inspect ID",
 		Short: "Displays secret details",
 		RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
@@ -81,7 +82,7 @@ func InspectSecret(dockerCli command.Cli) *cobra.Command {
 				return errors.New("Missing mandatory parameter: ID")
 			}
 			id := args[0]
-			secret, err := client.InspectSecret(context.Background(), id)
+			secret, err := backend.InspectSecret(context.Background(), id)
 			if err != nil {
 				return err
 			}
@@ -102,11 +103,11 @@ func ListSecrets(dockerCli command.Cli) *cobra.Command {
 		Aliases: []string{"ls"},
 		Short:   "List secrets stored for the existing account.",
 		RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
-			secrets, err := client.ListSecrets(context.Background())
+			secrets, err := backend.ListSecrets(context.Background())
 			if err != nil {
 				return err
 			}
@@ -125,21 +126,21 @@ func DeleteSecret(dockerCli command.Cli) *cobra.Command {
 		Aliases: []string{"rm", "remove"},
 		Short:   "Removes a secret.",
 		RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
-			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
 			if err != nil {
 				return err
 			}
 			if len(args) == 0 {
 				return errors.New("Missing mandatory parameter: [NAME]")
 			}
-			return client.DeleteSecret(context.Background(), args[0], opts.recover)
+			return backend.DeleteSecret(context.Background(), args[0], opts.recover)
 		}),
 	}
 	cmd.Flags().BoolVar(&opts.recover, "recover", false, "Enable recovery.")
 	return cmd
 }
 
-func printList(out io.Writer, secrets []docker.Secret) {
+func printList(out io.Writer, secrets []types.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)

+ 8 - 0
ecs/pkg/amazon/amazon.go

@@ -0,0 +1,8 @@
+package amazon
+
+import (
+	"github.com/docker/ecs-plugin/pkg/amazon/backend"
+	"github.com/docker/ecs-plugin/pkg/compose"
+)
+
+var _ compose.API = &backend.Backend{}

+ 0 - 11
ecs/pkg/amazon/api.go

@@ -1,11 +0,0 @@
-package amazon
-
-//go:generate mockgen -destination=./api_mock.go -self_package "github.com/docker/ecs-plugin/pkg/amazon" -package=amazon . API
-
-type API interface {
-	downAPI
-	upAPI
-	logsAPI
-	secretsAPI
-	listAPI
-}

+ 7 - 9
ecs/pkg/amazon/client.go → ecs/pkg/amazon/backend/backend.go

@@ -1,9 +1,9 @@
-package amazon
+package backend
 
 import (
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/session"
-	"github.com/docker/ecs-plugin/pkg/compose"
+	"github.com/docker/ecs-plugin/pkg/amazon/sdk"
 )
 
 const (
@@ -12,7 +12,7 @@ const (
 	ServiceTag = "com.docker.compose.service"
 )
 
-func NewClient(profile string, cluster string, region string) (compose.API, error) {
+func NewBackend(profile string, cluster string, region string) (*Backend, error) {
 	sess, err := session.NewSessionWithOptions(session.Options{
 		Profile: profile,
 		Config: aws.Config{
@@ -22,17 +22,15 @@ func NewClient(profile string, cluster string, region string) (compose.API, erro
 	if err != nil {
 		return nil, err
 	}
-	return &client{
+	return &Backend{
 		Cluster: cluster,
 		Region:  region,
-		api:     NewAPI(sess),
+		api:     sdk.NewAPI(sess),
 	}, nil
 }
 
-type client struct {
+type Backend struct {
 	Cluster string
 	Region  string
-	api     API
+	api     sdk.API
 }
-
-var _ compose.API = &client{}

+ 30 - 26
ecs/pkg/amazon/cloudformation.go → ecs/pkg/amazon/backend/cloudformation.go

@@ -1,4 +1,4 @@
-package amazon
+package backend
 
 import (
 	"fmt"
@@ -21,6 +21,9 @@ 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"
+	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"
 )
 
@@ -33,8 +36,8 @@ const (
 )
 
 // Convert a compose project into a CloudFormation template
-func (c client) Convert(project *compose.Project) (*cloudformation.Template, error) {
-	warnings := Check(project)
+func (b Backend) Convert(project *compose.Project) (*cloudformation.Template, error) {
+	warnings := compatibility.Check(project)
 	for _, w := range warnings {
 		logrus.Warn(w)
 	}
@@ -75,7 +78,7 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
 	// Create Cluster is `ParameterClusterName` parameter is not set
 	template.Conditions["CreateCluster"] = cloudformation.Equals("", cloudformation.Ref(ParameterClusterName))
 
-	cluster := c.createCluster(project, template)
+	cluster := createCluster(project, template)
 
 	networks := map[string]string{}
 	for _, net := range project.Networks {
@@ -88,17 +91,18 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
 	}
 
 	// Private DNS namespace will allow DNS name for the services to be <service>.<project>.local
-	c.createCloudMap(project, template)
+	createCloudMap(project, template)
 
-	loadBalancerARN := c.createLoadBalancer(project, template)
+	loadBalancerARN := createLoadBalancer(project, template)
 
 	for _, service := range project.Services {
-		definition, err := Convert(project, service)
+
+		definition, err := sdk.Convert(project, service)
 		if err != nil {
 			return nil, err
 		}
 
-		taskExecutionRole, err := c.createTaskExecutionRole(service, err, definition, template)
+		taskExecutionRole, err := createTaskExecutionRole(service, err, definition, template)
 		if err != nil {
 			return template, err
 		}
@@ -112,7 +116,7 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
 			// FIXME ECS only support HTTP(s) health checks, while Docker only support CMD
 		}
 
-		serviceRegistry := c.createServiceRegistry(service, template, healthCheck)
+		serviceRegistry := createServiceRegistry(service, template, healthCheck)
 
 		serviceSecurityGroups := []string{}
 		for net := range service.Networks {
@@ -124,14 +128,14 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
 		if len(service.Ports) > 0 {
 			for _, port := range service.Ports {
 				protocol := strings.ToUpper(port.Protocol)
-				if c.getLoadBalancerType(project) == elbv2.LoadBalancerTypeEnumApplication {
+				if getLoadBalancerType(project) == elbv2.LoadBalancerTypeEnumApplication {
 					protocol = elbv2.ProtocolEnumHttps
 					if port.Published == 80 {
 						protocol = elbv2.ProtocolEnumHttp
 					}
 				}
-				targetGroupName := c.createTargetGroup(project, service, port, template, protocol)
-				listenerName := c.createListener(service, port, template, targetGroupName, loadBalancerARN, protocol)
+				targetGroupName := createTargetGroup(project, service, port, template, protocol)
+				listenerName := createListener(service, port, template, targetGroupName, loadBalancerARN, protocol)
 				dependsOn = append(dependsOn, listenerName)
 				serviceLB = append(serviceLB, ecs.Service_LoadBalancer{
 					ContainerName:  service.Name,
@@ -184,7 +188,7 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
 	return template, nil
 }
 
-func (c client) getLoadBalancerType(project *compose.Project) string {
+func getLoadBalancerType(project *compose.Project) string {
 	for _, service := range project.Services {
 		for _, port := range service.Ports {
 			if port.Published != 80 && port.Published != 443 {
@@ -195,7 +199,7 @@ func (c client) getLoadBalancerType(project *compose.Project) string {
 	return elbv2.LoadBalancerTypeEnumApplication
 }
 
-func (c client) getLoadBalancerSecurityGroups(project *compose.Project, template *cloudformation.Template) []string {
+func getLoadBalancerSecurityGroups(project *compose.Project, template *cloudformation.Template) []string {
 	securityGroups := []string{}
 	for _, network := range project.Networks {
 		if !network.Internal {
@@ -206,15 +210,15 @@ func (c client) getLoadBalancerSecurityGroups(project *compose.Project, template
 	return uniqueStrings(securityGroups)
 }
 
-func (c client) createLoadBalancer(project *compose.Project, template *cloudformation.Template) string {
+func createLoadBalancer(project *compose.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))
 
-	loadBalancerType := c.getLoadBalancerType(project)
+	loadBalancerType := getLoadBalancerType(project)
 	securityGroups := []string{}
 	if loadBalancerType == elbv2.LoadBalancerTypeEnumApplication {
-		securityGroups = c.getLoadBalancerSecurityGroups(project, template)
+		securityGroups = getLoadBalancerSecurityGroups(project, template)
 	}
 
 	template.Resources[loadBalancerName] = &elasticloadbalancingv2.LoadBalancer{
@@ -237,7 +241,7 @@ func (c client) createLoadBalancer(project *compose.Project, template *cloudform
 	return cloudformation.If("CreateLoadBalancer", cloudformation.Ref(loadBalancerName), cloudformation.Ref(ParameterLoadBalancerARN))
 }
 
-func (c client) createListener(service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, targetGroupName string, loadBalancerARN string, protocol string) string {
+func createListener(service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, targetGroupName string, loadBalancerARN string, protocol string) string {
 	listenerName := fmt.Sprintf(
 		"%s%s%dListener",
 		normalizeResourceName(service.Name),
@@ -266,7 +270,7 @@ func (c client) createListener(service types.ServiceConfig, port types.ServicePo
 	return listenerName
 }
 
-func (c client) createTargetGroup(project *compose.Project, service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, protocol string) string {
+func createTargetGroup(project *compose.Project, service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, protocol string) string {
 	targetGroupName := fmt.Sprintf(
 		"%s%s%dTargetGroup",
 		normalizeResourceName(service.Name),
@@ -289,7 +293,7 @@ func (c client) createTargetGroup(project *compose.Project, service types.Servic
 	return targetGroupName
 }
 
-func (c client) createServiceRegistry(service types.ServiceConfig, template *cloudformation.Template, healthCheck *cloudmap.Service_HealthCheckConfig) ecs.Service_ServiceRegistry {
+func createServiceRegistry(service types.ServiceConfig, template *cloudformation.Template, healthCheck *cloudmap.Service_HealthCheckConfig) ecs.Service_ServiceRegistry {
 	serviceRegistration := fmt.Sprintf("%sServiceDiscoveryEntry", normalizeResourceName(service.Name))
 	serviceRegistry := ecs.Service_ServiceRegistry{
 		RegistryArn: cloudformation.GetAtt(serviceRegistration, "Arn"),
@@ -316,9 +320,9 @@ func (c client) createServiceRegistry(service types.ServiceConfig, template *clo
 	return serviceRegistry
 }
 
-func (c client) createTaskExecutionRole(service types.ServiceConfig, err error, definition *ecs.TaskDefinition, template *cloudformation.Template) (string, error) {
+func createTaskExecutionRole(service types.ServiceConfig, err error, definition *ecs.TaskDefinition, template *cloudformation.Template) (string, error) {
 	taskExecutionRole := fmt.Sprintf("%sTaskExecutionRole", normalizeResourceName(service.Name))
-	policy, err := c.getPolicy(definition)
+	policy, err := getPolicy(definition)
 	if err != nil {
 		return taskExecutionRole, err
 	}
@@ -341,7 +345,7 @@ func (c client) createTaskExecutionRole(service types.ServiceConfig, err error,
 	return taskExecutionRole, nil
 }
 
-func (c client) createCluster(project *compose.Project, template *cloudformation.Template) string {
+func createCluster(project *compose.Project, template *cloudformation.Template) string {
 	template.Resources["Cluster"] = &ecs.Cluster{
 		ClusterName: project.Name,
 		Tags: []tags.Tag{
@@ -356,7 +360,7 @@ func (c client) createCluster(project *compose.Project, template *cloudformation
 	return cluster
 }
 
-func (c client) createCloudMap(project *compose.Project, template *cloudformation.Template) {
+func createCloudMap(project *compose.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),
@@ -365,7 +369,7 @@ func (c client) createCloudMap(project *compose.Project, template *cloudformatio
 }
 
 func convertNetwork(project *compose.Project, net types.NetworkConfig, vpc string, template *cloudformation.Template) string {
-	if sg, ok := net.Extras[ExtensionSecurityGroup]; ok {
+	if sg, ok := net.Extras[btypes.ExtensionSecurityGroup]; ok {
 		logrus.Debugf("Security Group for network %q set by user to %q", net.Name, sg)
 		return sg.(string)
 	}
@@ -428,7 +432,7 @@ func normalizeResourceName(s string) string {
 	return strings.Title(regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString(s, ""))
 }
 
-func (c client) getPolicy(taskDef *ecs.TaskDefinition) (*PolicyDocument, error) {
+func getPolicy(taskDef *ecs.TaskDefinition) (*PolicyDocument, error) {
 	arns := []string{}
 	for _, container := range taskDef.ContainerDefinitions {
 		if container.RepositoryCredentials != nil {

+ 4 - 3
ecs/pkg/amazon/cloudformation_test.go → ecs/pkg/amazon/backend/cloudformation_test.go

@@ -1,4 +1,4 @@
-package amazon
+package backend
 
 import (
 	"fmt"
@@ -13,6 +13,7 @@ import (
 	"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"
 )
@@ -103,7 +104,7 @@ services:
 }
 
 func convertResultAsString(t *testing.T, project *compose.Project, clusterName string) string {
-	client, err := NewClient("", clusterName, "")
+	client, err := NewBackend("", clusterName, "")
 	assert.NilError(t, err)
 	result, err := client.Convert(project)
 	assert.NilError(t, err)
@@ -133,7 +134,7 @@ func convertYaml(t *testing.T, yaml string) *cloudformation.Template {
 	assert.NilError(t, err)
 	err = compose.Normalize(model)
 	assert.NilError(t, err)
-	template, err := client{}.Convert(&compose.Project{
+	template, err := Backend{}.Convert(&compose.Project{
 		Config: *model,
 		Name:   "test",
 	})

+ 31 - 0
ecs/pkg/amazon/backend/down.go

@@ -0,0 +1,31 @@
+package backend
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/docker/ecs-plugin/pkg/amazon/types"
+)
+
+func (b *Backend) ComposeDown(ctx context.Context, projectName string, deleteCluster bool) error {
+	err := b.api.DeleteStack(ctx, projectName)
+	if err != nil {
+		return err
+	}
+
+	err = b.WaitStackCompletion(ctx, projectName, types.StackDelete)
+	if err != nil {
+		return err
+	}
+
+	if !deleteCluster {
+		return nil
+	}
+
+	fmt.Printf("Delete cluster %s", b.Cluster)
+	if err = b.api.DeleteCluster(ctx, b.Cluster); err != nil {
+		return err
+	}
+	fmt.Printf("... done. \n")
+	return nil
+}

+ 9 - 7
ecs/pkg/amazon/down_test.go → ecs/pkg/amazon/backend/down_test.go

@@ -1,17 +1,19 @@
-package amazon
+package backend
 
 import (
 	"context"
 	"testing"
 
+	"github.com/docker/ecs-plugin/pkg/amazon/sdk"
+	btypes "github.com/docker/ecs-plugin/pkg/amazon/types"
 	"github.com/golang/mock/gomock"
 )
 
 func TestDownDontDeleteCluster(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
-	m := NewMockAPI(ctrl)
-	c := &client{
+	m := sdk.NewMockAPI(ctrl)
+	c := &Backend{
 		Cluster: "test_cluster",
 		Region:  "region",
 		api:     m,
@@ -20,7 +22,7 @@ func TestDownDontDeleteCluster(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", StackDelete).Return(nil)
+	recorder.WaitStackComplete(ctx, "stack-123", btypes.StackDelete).Return(nil)
 	recorder.DescribeStackEvents(ctx, "stack-123").Return(nil, nil)
 
 	c.ComposeDown(ctx, "test_project", false)
@@ -29,8 +31,8 @@ func TestDownDontDeleteCluster(t *testing.T) {
 func TestDownDeleteCluster(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
-	m := NewMockAPI(ctrl)
-	c := &client{
+	m := sdk.NewMockAPI(ctrl)
+	c := &Backend{
 		Cluster: "test_cluster",
 		Region:  "region",
 		api:     m,
@@ -40,7 +42,7 @@ func TestDownDeleteCluster(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", StackDelete).Return(nil)
+	recorder.WaitStackComplete(ctx, "stack-123", btypes.StackDelete).Return(nil)
 	recorder.DescribeStackEvents(ctx, "stack-123").Return(nil, nil)
 	recorder.DeleteCluster(ctx, "test_cluster").Return(nil)
 

+ 1 - 1
ecs/pkg/amazon/iam.go → ecs/pkg/amazon/backend/iam.go

@@ -1,4 +1,4 @@
-package amazon
+package backend
 
 const (
 	ECSTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"

+ 63 - 0
ecs/pkg/amazon/backend/list.go

@@ -0,0 +1,63 @@
+package backend
+
+import (
+	"context"
+	"fmt"
+	"sort"
+	"strings"
+
+	"github.com/docker/ecs-plugin/pkg/amazon/types"
+	"github.com/docker/ecs-plugin/pkg/compose"
+)
+
+func (b *Backend) ComposePs(ctx context.Context, project *compose.Project) ([]types.TaskStatus, error) {
+	cluster := b.Cluster
+	if cluster == "" {
+		cluster = project.Name
+	}
+	arns := []string{}
+	for _, service := range project.Services {
+		tasks, err := b.api.ListTasks(ctx, cluster, service.Name)
+		if err != nil {
+			return []types.TaskStatus{}, err
+		}
+		arns = append(arns, tasks...)
+	}
+	if len(arns) == 0 {
+		return []types.TaskStatus{}, nil
+	}
+
+	tasks, err := b.api.DescribeTasks(ctx, cluster, arns...)
+	if err != nil {
+		return []types.TaskStatus{}, err
+	}
+
+	networkInterfaces := []string{}
+	for _, t := range tasks {
+		if t.NetworkInterface != "" {
+			networkInterfaces = append(networkInterfaces, t.NetworkInterface)
+		}
+	}
+	publicIps, err := b.api.GetPublicIPs(ctx, networkInterfaces...)
+	if err != nil {
+		return []types.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 {
+		ports := []string{}
+		s, err := project.GetService(t.Service)
+		if err != nil {
+			return []types.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))
+		}
+		tasks[i].Name = s.Name
+		tasks[i].Ports = ports
+	}
+	return tasks, nil
+}

+ 6 - 14
ecs/pkg/amazon/logs.go → ecs/pkg/amazon/backend/logs.go

@@ -1,4 +1,4 @@
-package amazon
+package backend
 
 import (
 	"context"
@@ -11,8 +11,8 @@ import (
 	"github.com/docker/ecs-plugin/pkg/console"
 )
 
-func (c *client) ComposeLogs(ctx context.Context, projectName string) error {
-	err := c.api.GetLogs(ctx, projectName, &logConsumer{
+func (b *Backend) ComposeLogs(ctx context.Context, projectName string) error {
+	err := b.api.GetLogs(ctx, projectName, &logConsumer{
 		colors: map[string]console.ColorFunc{},
 		width:  0,
 	})
@@ -26,11 +26,6 @@ func (c *client) ComposeLogs(ctx context.Context, projectName string) error {
 	return nil
 }
 
-type logConsumer struct {
-	colors map[string]console.ColorFunc
-	width  int
-}
-
 func (l *logConsumer) Log(service, container, message string) {
 	cf, ok := l.colors[service]
 	if !ok {
@@ -54,10 +49,7 @@ func (l *logConsumer) computeWidth() {
 	l.width = width + 3
 }
 
-type LogConsumer interface {
-	Log(service, container, message string)
-}
-
-type logsAPI interface {
-	GetLogs(ctx context.Context, name string, consumer LogConsumer) error
+type logConsumer struct {
+	colors map[string]console.ColorFunc
+	width  int
 }

+ 23 - 0
ecs/pkg/amazon/backend/secrets.go

@@ -0,0 +1,23 @@
+package backend
+
+import (
+	"context"
+
+	"github.com/docker/ecs-plugin/pkg/amazon/types"
+)
+
+func (b Backend) CreateSecret(ctx context.Context, secret types.Secret) (string, error) {
+	return b.api.CreateSecret(ctx, secret)
+}
+
+func (b Backend) InspectSecret(ctx context.Context, id string) (types.Secret, error) {
+	return b.api.InspectSecret(ctx, id)
+}
+
+func (b Backend) ListSecrets(ctx context.Context) ([]types.Secret, error) {
+	return b.api.ListSecrets(ctx)
+}
+
+func (b Backend) DeleteSecret(ctx context.Context, id string, recover bool) error {
+	return b.api.DeleteSecret(ctx, id, recover)
+}

+ 0 - 0
ecs/pkg/amazon/testdata/input/simple-single-service-with-overrides.yaml → ecs/pkg/amazon/backend/testdata/input/simple-single-service-with-overrides.yaml


+ 0 - 0
ecs/pkg/amazon/testdata/input/simple-single-service.yaml → ecs/pkg/amazon/backend/testdata/input/simple-single-service.yaml


+ 0 - 0
ecs/pkg/amazon/testdata/invalid_network_mode.yaml → ecs/pkg/amazon/backend/testdata/invalid_network_mode.yaml


+ 0 - 0
ecs/pkg/amazon/testdata/simple/simple-cloudformation-conversion.golden → ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-conversion.golden


+ 0 - 0
ecs/pkg/amazon/testdata/simple/simple-cloudformation-with-overrides-conversion.golden → ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-with-overrides-conversion.golden


+ 100 - 0
ecs/pkg/amazon/backend/up.go

@@ -0,0 +1,100 @@
+package backend
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/docker/ecs-plugin/pkg/amazon/types"
+	"github.com/docker/ecs-plugin/pkg/compose"
+)
+
+func (b *Backend) ComposeUp(ctx context.Context, project *compose.Project) error {
+	if b.Cluster != "" {
+		ok, err := b.api.ClusterExists(ctx, b.Cluster)
+		if err != nil {
+			return err
+		}
+		if !ok {
+			return fmt.Errorf("configured cluster %q does not exist", b.Cluster)
+		}
+	}
+
+	update, err := b.api.StackExists(ctx, project.Name)
+	if err != nil {
+		return err
+	}
+	if update {
+		return fmt.Errorf("we do not (yet) support updating an existing CloudFormation stack")
+	}
+
+	template, err := b.Convert(project)
+	if err != nil {
+		return err
+	}
+
+	vpc, err := b.GetVPC(ctx, project)
+	if err != nil {
+		return err
+	}
+
+	subNets, err := b.api.GetSubNets(ctx, vpc)
+	if err != nil {
+		return err
+	}
+
+	lb, err := b.GetLoadBalancer(ctx, project)
+	if err != nil {
+		return err
+	}
+
+	parameters := map[string]string{
+		ParameterClusterName:     b.Cluster,
+		ParameterVPCId:           vpc,
+		ParameterSubnet1Id:       subNets[0],
+		ParameterSubnet2Id:       subNets[1],
+		ParameterLoadBalancerARN: lb,
+	}
+
+	err = b.api.CreateStack(ctx, project.Name, template, parameters)
+	if err != nil {
+		return err
+	}
+
+	fmt.Println()
+	return b.WaitStackCompletion(ctx, project.Name, types.StackCreate)
+}
+
+func (b Backend) GetVPC(ctx context.Context, project *compose.Project) (string, error) {
+	//check compose file for custom VPC selected
+	if vpc, ok := project.Extras[types.ExtensionVPC]; ok {
+		vpcID := vpc.(string)
+		ok, err := b.api.VpcExists(ctx, vpcID)
+		if err != nil {
+			return "", err
+		}
+		if !ok {
+			return "", fmt.Errorf("VPC does not exist: %s", vpc)
+		}
+	}
+	defaultVPC, err := b.api.GetDefaultVPC(ctx)
+	if err != nil {
+		return "", err
+	}
+	return defaultVPC, nil
+}
+
+func (b Backend) GetLoadBalancer(ctx context.Context, project *compose.Project) (string, error) {
+	//check compose file for custom VPC selected
+	if lb, ok := project.Extras[types.ExtensionLB]; ok {
+		lbName := lb.(string)
+		ok, err := b.api.LoadBalancerExists(ctx, lbName)
+		if err != nil {
+			return "", err
+		}
+		if !ok {
+			return "", fmt.Errorf("Load Balancer does not exist: %s", lb)
+		}
+		return b.api.GetLoadBalancerARN(ctx, lbName)
+	}
+	return "", nil
+}

+ 5 - 17
ecs/pkg/amazon/wait.go → ecs/pkg/amazon/backend/wait.go

@@ -1,4 +1,4 @@
-package amazon
+package backend
 
 import (
 	"context"
@@ -8,16 +8,15 @@ import (
 	"time"
 
 	"github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/service/cloudformation"
 	"github.com/docker/ecs-plugin/pkg/console"
 )
 
-func (c *client) WaitStackCompletion(ctx context.Context, name string, operation int) error {
+func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operation int) error {
 	w := console.NewProgressWriter()
 	knownEvents := map[string]struct{}{}
 
 	// Get the unique Stack ID so we can collect events without getting some from previous deployments with same name
-	stackID, err := c.api.GetStackID(ctx, name)
+	stackID, err := b.api.GetStackID(ctx, name)
 	if err != nil {
 		return err
 	}
@@ -26,7 +25,7 @@ func (c *client) WaitStackCompletion(ctx context.Context, name string, operation
 	done := make(chan bool)
 
 	go func() {
-		c.api.WaitStackComplete(ctx, stackID, operation) //nolint:errcheck
+		b.api.WaitStackComplete(ctx, stackID, operation) //nolint:errcheck
 		ticker.Stop()
 		done <- true
 	}()
@@ -39,7 +38,7 @@ func (c *client) WaitStackCompletion(ctx context.Context, name string, operation
 			completed = true
 		case <-ticker.C:
 		}
-		events, err := c.api.DescribeStackEvents(ctx, stackID)
+		events, err := b.api.DescribeStackEvents(ctx, stackID)
 		if err != nil {
 			return err
 		}
@@ -65,14 +64,3 @@ func (c *client) WaitStackCompletion(ctx context.Context, name string, operation
 	}
 	return stackErr
 }
-
-type waitAPI interface {
-	GetStackID(ctx context.Context, name string) (string, error)
-	WaitStackComplete(ctx context.Context, name string, operation int) error
-	DescribeStackEvents(ctx context.Context, stackID string) ([]*cloudformation.StackEvent, error)
-}
-
-const (
-	StackCreate = iota
-	StackDelete
-)

+ 0 - 13
ecs/pkg/amazon/check_test.go

@@ -1,13 +0,0 @@
-package amazon
-
-import (
-	"testing"
-
-	"gotest.tools/v3/assert"
-)
-
-func TestInvalidNetworkMode(t *testing.T) {
-	project := load(t, "testdata/invalid_network_mode.yaml")
-	err := Check(project)
-	assert.Error(t, err[0], "'network_mode' \"bridge\" is not supported")
-}

+ 1 - 1
ecs/pkg/amazon/check.go → ecs/pkg/amazon/compatibility/check.go

@@ -1,4 +1,4 @@
-package amazon
+package compatibility
 
 import (
 	"github.com/compose-spec/compose-go/types"

+ 23 - 0
ecs/pkg/amazon/compatibility/check_test.go

@@ -0,0 +1,23 @@
+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")
+}

+ 1 - 1
ecs/pkg/amazon/compatibility.go → ecs/pkg/amazon/compatibility/compatibility.go

@@ -1,4 +1,4 @@
-package amazon
+package compatibility
 
 import (
 	"fmt"

+ 0 - 34
ecs/pkg/amazon/down.go

@@ -1,34 +0,0 @@
-package amazon
-
-import (
-	"context"
-	"fmt"
-)
-
-func (c *client) ComposeDown(ctx context.Context, projectName string, deleteCluster bool) error {
-	err := c.api.DeleteStack(ctx, projectName)
-	if err != nil {
-		return err
-	}
-
-	err = c.WaitStackCompletion(ctx, projectName, StackDelete)
-	if err != nil {
-		return err
-	}
-
-	if !deleteCluster {
-		return nil
-	}
-
-	fmt.Printf("Delete cluster %s", c.Cluster)
-	if err = c.api.DeleteCluster(ctx, c.Cluster); err != nil {
-		return err
-	}
-	fmt.Printf("... done. \n")
-	return nil
-}
-
-type downAPI interface {
-	DeleteStack(ctx context.Context, name string) error
-	DeleteCluster(ctx context.Context, name string) error
-}

+ 0 - 80
ecs/pkg/amazon/list.go

@@ -1,80 +0,0 @@
-package amazon
-
-import (
-	"context"
-	"fmt"
-	"os"
-	"sort"
-	"strings"
-	"text/tabwriter"
-
-	"github.com/docker/ecs-plugin/pkg/compose"
-)
-
-func (c *client) ComposePs(ctx context.Context, project *compose.Project) error {
-	cluster := c.Cluster
-	if cluster == "" {
-		cluster = project.Name
-	}
-	w := tabwriter.NewWriter(os.Stdout, 20, 2, 3, ' ', 0)
-	fmt.Fprintf(w, "Name\tState\tPorts\n")
-	defer w.Flush()
-
-	arns := []string{}
-	for _, service := range project.Services {
-		tasks, err := c.api.ListTasks(ctx, cluster, service.Name)
-		if err != nil {
-			return err
-		}
-		arns = append(arns, tasks...)
-	}
-	if len(arns) == 0 {
-		return nil
-	}
-
-	tasks, err := c.api.DescribeTasks(ctx, cluster, arns...)
-	if err != nil {
-		return err
-	}
-
-	networkInterfaces := []string{}
-	for _, t := range tasks {
-		if t.NetworkInterface != "" {
-			networkInterfaces = append(networkInterfaces, t.NetworkInterface)
-		}
-	}
-	publicIps, err := c.api.GetPublicIPs(ctx, networkInterfaces...)
-	if err != nil {
-		return err
-	}
-
-	sort.Slice(tasks, func(i, j int) bool {
-		return strings.Compare(tasks[i].Service, tasks[j].Service) < 0
-	})
-
-	for _, t := range tasks {
-		ports := []string{}
-		s, err := project.GetService(t.Service)
-		if err != nil {
-			return err
-		}
-		for _, p := range s.Ports {
-			ports = append(ports, fmt.Sprintf("%s:%d->%d/%s", publicIps[t.NetworkInterface], p.Published, p.Target, p.Protocol))
-		}
-		fmt.Fprintf(w, "%s\t%s\t%s\n", s.Name, t.State, strings.Join(ports, ", "))
-	}
-	return nil
-}
-
-type TaskStatus struct {
-	State            string
-	Service          string
-	NetworkInterface string
-	PublicIP         string
-}
-
-type listAPI interface {
-	ListTasks(ctx context.Context, cluster string, name string) ([]string, error)
-	DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]TaskStatus, error)
-	GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error)
-}

+ 61 - 0
ecs/pkg/amazon/sdk/api.go

@@ -0,0 +1,61 @@
+package sdk
+
+import (
+	"context"
+
+	cf "github.com/aws/aws-sdk-go/service/cloudformation"
+	"github.com/awslabs/goformation/v4/cloudformation"
+	"github.com/docker/ecs-plugin/pkg/amazon/types"
+)
+
+//go:generate mockgen -destination=./api_mock.go -self_package "github.com/docker/ecs-plugin/pkg/amazon" -package=amazon . API
+
+type API interface {
+	downAPI
+	upAPI
+	logsAPI
+	secretsAPI
+	listAPI
+}
+
+type upAPI interface {
+	waitAPI
+	GetDefaultVPC(ctx context.Context) (string, error)
+	VpcExists(ctx context.Context, vpcID string) (bool, error)
+	GetSubNets(ctx context.Context, vpcID string) ([]string, error)
+
+	ClusterExists(ctx context.Context, name string) (bool, error)
+	StackExists(ctx context.Context, name string) (bool, error)
+	CreateStack(ctx context.Context, name string, template *cloudformation.Template, parameters map[string]string) error
+
+	LoadBalancerExists(ctx context.Context, name string) (bool, error)
+	GetLoadBalancerARN(ctx context.Context, name string) (string, error)
+}
+
+type downAPI interface {
+	DeleteStack(ctx context.Context, name string) error
+	DeleteCluster(ctx context.Context, name string) error
+}
+
+type logsAPI interface {
+	GetLogs(ctx context.Context, name string, consumer types.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)
+	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)
+	GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error)
+}
+
+type waitAPI interface {
+	GetStackID(ctx context.Context, name string) (string, error)
+	WaitStackComplete(ctx context.Context, name string, operation int) error
+	DescribeStackEvents(ctx context.Context, stackID string) ([]*cf.StackEvent, error)
+}

+ 10 - 10
ecs/pkg/amazon/api_mock.go → ecs/pkg/amazon/sdk/api_mock.go

@@ -2,7 +2,7 @@
 // Source: github.com/docker/ecs-plugin/pkg/amazon (interfaces: API)
 
 // Package amazon is a generated GoMock package.
-package amazon
+package sdk
 
 import (
 	context "context"
@@ -10,7 +10,7 @@ import (
 
 	cloudformation "github.com/aws/aws-sdk-go/service/cloudformation"
 	cloudformation0 "github.com/awslabs/goformation/v4/cloudformation"
-	docker "github.com/docker/ecs-plugin/pkg/docker"
+	btypes "github.com/docker/ecs-plugin/pkg/amazon/types"
 	gomock "github.com/golang/mock/gomock"
 )
 
@@ -53,7 +53,7 @@ func (mr *MockAPIMockRecorder) ClusterExists(arg0, arg1 interface{}) *gomock.Cal
 }
 
 // CreateSecret mocks base method
-func (m *MockAPI) CreateSecret(arg0 context.Context, arg1 docker.Secret) (string, error) {
+func (m *MockAPI) CreateSecret(arg0 context.Context, arg1 btypes.Secret) (string, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "CreateSecret", arg0, arg1)
 	ret0, _ := ret[0].(string)
@@ -139,14 +139,14 @@ func (mr *MockAPIMockRecorder) DescribeStackEvents(arg0, arg1 interface{}) *gomo
 }
 
 // DescribeTasks mocks base method
-func (m *MockAPI) DescribeTasks(arg0 context.Context, arg1 string, arg2 ...string) ([]TaskStatus, error) {
+func (m *MockAPI) DescribeTasks(arg0 context.Context, arg1 string, arg2 ...string) ([]btypes.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].([]TaskStatus)
+	ret0, _ := ret[0].([]btypes.TaskStatus)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
@@ -174,7 +174,7 @@ func (mr *MockAPIMockRecorder) GetDefaultVPC(arg0 interface{}) *gomock.Call {
 }
 
 // GetLogs mocks base method
-func (m *MockAPI) GetLogs(arg0 context.Context, arg1 string, arg2 LogConsumer) error {
+func (m *MockAPI) GetLogs(arg0 context.Context, arg1 string, arg2 btypes.LogConsumer) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetLogs", arg0, arg1, arg2)
 	ret0, _ := ret[0].(error)
@@ -238,10 +238,10 @@ func (mr *MockAPIMockRecorder) GetSubNets(arg0, arg1 interface{}) *gomock.Call {
 }
 
 // InspectSecret mocks base method
-func (m *MockAPI) InspectSecret(arg0 context.Context, arg1 string) (docker.Secret, error) {
+func (m *MockAPI) InspectSecret(arg0 context.Context, arg1 string) (btypes.Secret, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "InspectSecret", arg0, arg1)
-	ret0, _ := ret[0].(docker.Secret)
+	ret0, _ := ret[0].(btypes.Secret)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
@@ -253,10 +253,10 @@ func (mr *MockAPIMockRecorder) InspectSecret(arg0, arg1 interface{}) *gomock.Cal
 }
 
 // ListSecrets mocks base method
-func (m *MockAPI) ListSecrets(arg0 context.Context) ([]docker.Secret, error) {
+func (m *MockAPI) ListSecrets(arg0 context.Context) ([]btypes.Secret, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ListSecrets", arg0)
-	ret0, _ := ret[0].([]docker.Secret)
+	ret0, _ := ret[0].([]btypes.Secret)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }

+ 3 - 2
ecs/pkg/amazon/convert.go → ecs/pkg/amazon/sdk/convert.go

@@ -1,4 +1,4 @@
-package amazon
+package sdk
 
 import (
 	"fmt"
@@ -13,6 +13,7 @@ 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"
 )
 
@@ -318,7 +319,7 @@ 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 == ExtensionPullCredentials {
+		if key == t.ExtensionPullCredentials {
 			return &ecs.TaskDefinition_RepositoryCredentials{CredentialsParameter: value.(string)}
 		}
 	}

+ 17 - 16
ecs/pkg/amazon/sdk.go → ecs/pkg/amazon/sdk/sdk.go

@@ -1,4 +1,4 @@
-package amazon
+package sdk
 
 import (
 	"context"
@@ -25,7 +25,8 @@ import (
 	cf "github.com/awslabs/goformation/v4/cloudformation"
 	"github.com/sirupsen/logrus"
 
-	"github.com/docker/ecs-plugin/pkg/docker"
+	"github.com/docker/ecs-plugin/pkg/amazon/types"
+	t "github.com/docker/ecs-plugin/pkg/amazon/types"
 )
 
 type sdk struct {
@@ -188,9 +189,9 @@ func (s sdk) WaitStackComplete(ctx context.Context, name string, operation int)
 		StackName: aws.String(name),
 	}
 	switch operation {
-	case StackCreate:
+	case t.StackCreate:
 		return s.CF.WaitUntilStackCreateCompleteWithContext(ctx, input)
-	case StackDelete:
+	case t.StackDelete:
 		return s.CF.WaitUntilStackDeleteCompleteWithContext(ctx, input)
 	default:
 		return fmt.Errorf("internal error: unexpected stack operation %d", operation)
@@ -235,7 +236,7 @@ func (s sdk) DeleteStack(ctx context.Context, name string) error {
 	return err
 }
 
-func (s sdk) CreateSecret(ctx context.Context, secret docker.Secret) (string, error) {
+func (s sdk) CreateSecret(ctx context.Context, secret t.Secret) (string, error) {
 	logrus.Debug("Create secret " + secret.Name)
 	secretStr, err := secret.GetCredString()
 	if err != nil {
@@ -253,17 +254,17 @@ func (s sdk) CreateSecret(ctx context.Context, secret docker.Secret) (string, er
 	return *response.ARN, nil
 }
 
-func (s sdk) InspectSecret(ctx context.Context, id string) (docker.Secret, error) {
+func (s sdk) InspectSecret(ctx context.Context, id string) (t.Secret, error) {
 	logrus.Debug("Inspect secret " + id)
 	response, err := s.SM.DescribeSecret(&secretsmanager.DescribeSecretInput{SecretId: &id})
 	if err != nil {
-		return docker.Secret{}, err
+		return t.Secret{}, err
 	}
 	labels := map[string]string{}
 	for _, tag := range response.Tags {
 		labels[*tag.Key] = *tag.Value
 	}
-	secret := docker.Secret{
+	secret := t.Secret{
 		ID:     *response.ARN,
 		Name:   *response.Name,
 		Labels: labels,
@@ -274,14 +275,14 @@ func (s sdk) InspectSecret(ctx context.Context, id string) (docker.Secret, error
 	return secret, nil
 }
 
-func (s sdk) ListSecrets(ctx context.Context) ([]docker.Secret, error) {
+func (s sdk) ListSecrets(ctx context.Context) ([]t.Secret, error) {
 
 	logrus.Debug("List secrets ...")
 	response, err := s.SM.ListSecrets(&secretsmanager.ListSecretsInput{})
 	if err != nil {
-		return []docker.Secret{}, err
+		return []t.Secret{}, err
 	}
-	var secrets []docker.Secret
+	var secrets []t.Secret
 
 	for _, sec := range response.SecretList {
 
@@ -293,7 +294,7 @@ func (s sdk) ListSecrets(ctx context.Context) ([]docker.Secret, error) {
 		if sec.Description != nil {
 			description = *sec.Description
 		}
-		secrets = append(secrets, docker.Secret{
+		secrets = append(secrets, t.Secret{
 			ID:          *sec.ARN,
 			Name:        *sec.Name,
 			Labels:      labels,
@@ -310,7 +311,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 LogConsumer) error {
+func (s sdk) GetLogs(ctx context.Context, name string, consumer types.LogConsumer) error {
 	logGroup := fmt.Sprintf("/docker-compose/%s", name)
 	var startTime int64
 	for {
@@ -356,7 +357,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) ([]TaskStatus, error) {
+func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]t.TaskStatus, error) {
 	tasks, err := s.ECS.DescribeTasksWithContext(ctx, &ecs.DescribeTasksInput{
 		Cluster: aws.String(cluster),
 		Tasks:   aws.StringSlice(arns),
@@ -364,7 +365,7 @@ func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string)
 	if err != nil {
 		return nil, err
 	}
-	result := []TaskStatus{}
+	result := []t.TaskStatus{}
 	for _, task := range tasks.Tasks {
 		var networkInterface string
 		for _, attachement := range task.Attachments {
@@ -376,7 +377,7 @@ func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string)
 				}
 			}
 		}
-		result = append(result, TaskStatus{
+		result = append(result, t.TaskStatus{
 			State:            *task.LastStatus,
 			Service:          strings.Replace(*task.Group, "service:", "", 1),
 			NetworkInterface: networkInterface,

+ 0 - 30
ecs/pkg/amazon/secrets.go

@@ -1,30 +0,0 @@
-package amazon
-
-import (
-	"context"
-
-	"github.com/docker/ecs-plugin/pkg/docker"
-)
-
-type secretsAPI interface {
-	CreateSecret(ctx context.Context, secret docker.Secret) (string, error)
-	InspectSecret(ctx context.Context, id string) (docker.Secret, error)
-	ListSecrets(ctx context.Context) ([]docker.Secret, error)
-	DeleteSecret(ctx context.Context, id string, recover bool) error
-}
-
-func (c client) CreateSecret(ctx context.Context, secret docker.Secret) (string, error) {
-	return c.api.CreateSecret(ctx, secret)
-}
-
-func (c client) InspectSecret(ctx context.Context, id string) (docker.Secret, error) {
-	return c.api.InspectSecret(ctx, id)
-}
-
-func (c client) ListSecrets(ctx context.Context) ([]docker.Secret, error) {
-	return c.api.ListSecrets(ctx)
-}
-
-func (c client) DeleteSecret(ctx context.Context, id string, recover bool) error {
-	return c.api.DeleteSecret(ctx, id, recover)
-}

+ 19 - 3
ecs/pkg/docker/secret.go → ecs/pkg/amazon/types/types.go

@@ -1,9 +1,25 @@
-package docker
+package types
 
-import (
-	"encoding/json"
+import "encoding/json"
+
+type TaskStatus struct {
+	Name             string
+	State            string
+	Service          string
+	NetworkInterface string
+	PublicIP         string
+	Ports            []string
+}
+
+const (
+	StackCreate = iota
+	StackDelete
 )
 
+type LogConsumer interface {
+	Log(service, container, message string)
+}
+
 type Secret struct {
 	ID          string            `json:"ID"`
 	Name        string            `json:"Name"`

+ 1 - 1
ecs/pkg/amazon/x.go → ecs/pkg/amazon/types/x.go

@@ -1,4 +1,4 @@
-package amazon
+package types
 
 const (
 	ExtensionSecurityGroup   = "x-aws-securitygroup"

+ 0 - 114
ecs/pkg/amazon/up.go

@@ -1,114 +0,0 @@
-package amazon
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/awslabs/goformation/v4/cloudformation"
-	"github.com/docker/ecs-plugin/pkg/compose"
-)
-
-func (c *client) ComposeUp(ctx context.Context, project *compose.Project) error {
-	if c.Cluster != "" {
-		ok, err := c.api.ClusterExists(ctx, c.Cluster)
-		if err != nil {
-			return err
-		}
-		if !ok {
-			return fmt.Errorf("configured cluster %q does not exist", c.Cluster)
-		}
-	}
-
-	update, err := c.api.StackExists(ctx, project.Name)
-	if err != nil {
-		return err
-	}
-	if update {
-		return fmt.Errorf("we do not (yet) support updating an existing CloudFormation stack")
-	}
-
-	template, err := c.Convert(project)
-	if err != nil {
-		return err
-	}
-
-	vpc, err := c.GetVPC(ctx, project)
-	if err != nil {
-		return err
-	}
-
-	subNets, err := c.api.GetSubNets(ctx, vpc)
-	if err != nil {
-		return err
-	}
-
-	lb, err := c.GetLoadBalancer(ctx, project)
-	if err != nil {
-		return err
-	}
-
-	parameters := map[string]string{
-		ParameterClusterName:     c.Cluster,
-		ParameterVPCId:           vpc,
-		ParameterSubnet1Id:       subNets[0],
-		ParameterSubnet2Id:       subNets[1],
-		ParameterLoadBalancerARN: lb,
-	}
-
-	err = c.api.CreateStack(ctx, project.Name, template, parameters)
-	if err != nil {
-		return err
-	}
-
-	fmt.Println()
-	return c.WaitStackCompletion(ctx, project.Name, StackCreate)
-}
-
-func (c client) GetVPC(ctx context.Context, project *compose.Project) (string, error) {
-	//check compose file for custom VPC selected
-	if vpc, ok := project.Extras[ExtensionVPC]; ok {
-		vpcID := vpc.(string)
-		ok, err := c.api.VpcExists(ctx, vpcID)
-		if err != nil {
-			return "", err
-		}
-		if !ok {
-			return "", fmt.Errorf("VPC does not exist: %s", vpc)
-		}
-	}
-	defaultVPC, err := c.api.GetDefaultVPC(ctx)
-	if err != nil {
-		return "", err
-	}
-	return defaultVPC, nil
-}
-
-func (c client) GetLoadBalancer(ctx context.Context, project *compose.Project) (string, error) {
-	//check compose file for custom VPC selected
-	if lb, ok := project.Extras[ExtensionLB]; ok {
-		lbName := lb.(string)
-		ok, err := c.api.LoadBalancerExists(ctx, lbName)
-		if err != nil {
-			return "", err
-		}
-		if !ok {
-			return "", fmt.Errorf("Load Balancer does not exist: %s", lb)
-		}
-		return c.api.GetLoadBalancerARN(ctx, lbName)
-	}
-	return "", nil
-}
-
-type upAPI interface {
-	waitAPI
-	GetDefaultVPC(ctx context.Context) (string, error)
-	VpcExists(ctx context.Context, vpcID string) (bool, error)
-	GetSubNets(ctx context.Context, vpcID string) ([]string, error)
-
-	ClusterExists(ctx context.Context, name string) (bool, error)
-	StackExists(ctx context.Context, name string) (bool, error)
-	CreateStack(ctx context.Context, name string, template *cloudformation.Template, parameters map[string]string) error
-
-	LoadBalancerExists(ctx context.Context, name string) (bool, error)
-	GetLoadBalancerARN(ctx context.Context, name string) (string, error)
-}

+ 5 - 5
ecs/pkg/compose/api.go

@@ -4,7 +4,7 @@ import (
 	"context"
 
 	"github.com/awslabs/goformation/v4/cloudformation"
-	"github.com/docker/ecs-plugin/pkg/docker"
+	"github.com/docker/ecs-plugin/pkg/amazon/types"
 )
 
 type API interface {
@@ -13,9 +13,9 @@ type API interface {
 	ComposeDown(ctx context.Context, projectName string, deleteCluster bool) error
 	ComposeLogs(ctx context.Context, projectName string) error
 
-	CreateSecret(ctx context.Context, secret docker.Secret) (string, error)
-	InspectSecret(ctx context.Context, id string) (docker.Secret, error)
-	ListSecrets(ctx context.Context) ([]docker.Secret, 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)
 	DeleteSecret(ctx context.Context, id string, recover bool) error
-	ComposePs(background context.Context, project *Project) error
+	ComposePs(background context.Context, project *Project) ([]types.TaskStatus, error)
 }