Browse Source

`ps` do list services, not containers

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 5 years ago
parent
commit
a1eba59a46

+ 5 - 5
ecs/cmd/commands/compose.go

@@ -101,15 +101,15 @@ func PsCommand(dockerCli command.Cli, projectOpts *cli.ProjectOptions) *cobra.Co
 			if err != nil {
 				return err
 			}
-			tasks, err := backend.Ps(context.Background(), project)
+			status, err := backend.Ps(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, " "))
+			printSection(os.Stdout, len(status), func(w io.Writer) {
+				for _, service := range status {
+					fmt.Fprintf(w, "%s\t%s\t%d/%d\t%s\n", service.ID, service.Name, service.Replicas, service.Desired, strings.Join(service.Ports, " "))
 				}
-			}, "NAME", "STATE", "PORTS")
+			}, "ID", "NAME", "REPLICAS", "PORTS")
 			return nil
 		}),
 	}

+ 1 - 2
ecs/pkg/amazon/backend/cloudformation.go

@@ -18,7 +18,6 @@ import (
 	"github.com/awslabs/goformation/v4/cloudformation/tags"
 	"github.com/compose-spec/compose-go/compatibility"
 	"github.com/compose-spec/compose-go/types"
-	sdk "github.com/docker/ecs-plugin/pkg/amazon/sdk"
 	"github.com/docker/ecs-plugin/pkg/compose"
 	"github.com/sirupsen/logrus"
 )
@@ -120,7 +119,7 @@ func (b Backend) Convert(project *types.Project) (*cloudformation.Template, erro
 
 	for _, service := range project.Services {
 
-		definition, err := sdk.Convert(project, service)
+		definition, err := Convert(project, service)
 		if err != nil {
 			return nil, err
 		}

+ 1 - 1
ecs/pkg/amazon/sdk/convert.go → ecs/pkg/amazon/backend/convert.go

@@ -1,4 +1,4 @@
-package sdk
+package backend
 
 import (
 	"fmt"

+ 10 - 42
ecs/pkg/amazon/backend/list.go

@@ -3,61 +3,29 @@ package backend
 import (
 	"context"
 	"fmt"
-	"sort"
-	"strings"
 
 	"github.com/compose-spec/compose-go/types"
 	"github.com/docker/ecs-plugin/pkg/compose"
 )
 
-func (b *Backend) Ps(ctx context.Context, project *types.Project) ([]compose.TaskStatus, error) {
+func (b *Backend) Ps(ctx context.Context, project *types.Project) ([]compose.ServiceStatus, error) {
 	cluster := b.Cluster
 	if cluster == "" {
 		cluster = project.Name
 	}
-	arns := []string{}
+
+	status := []compose.ServiceStatus{}
 	for _, service := range project.Services {
-		tasks, err := b.api.ListTasks(ctx, cluster, service.Name)
+		desc, err := b.api.DescribeService(ctx, cluster, service.Name)
 		if err != nil {
-			return []compose.TaskStatus{}, err
-		}
-		arns = append(arns, tasks...)
-	}
-	if len(arns) == 0 {
-		return []compose.TaskStatus{}, nil
-	}
-
-	tasks, err := b.api.DescribeTasks(ctx, cluster, arns...)
-	if err != nil {
-		return []compose.TaskStatus{}, err
-	}
-
-	networkInterfaces := []string{}
-	for _, t := range tasks {
-		if t.NetworkInterface != "" {
-			networkInterfaces = append(networkInterfaces, t.NetworkInterface)
+			return nil, err
 		}
-	}
-	publicIps, err := b.api.GetPublicIPs(ctx, networkInterfaces...)
-	if err != nil {
-		return []compose.TaskStatus{}, err
-	}
-
-	sort.Slice(tasks, func(i, j int) bool {
-		return strings.Compare(tasks[i].Service, tasks[j].Service) < 0
-	})
-
-	for i, task := range tasks {
 		ports := []string{}
-		s, err := project.GetService(task.Service)
-		if err != nil {
-			return []compose.TaskStatus{}, err
-		}
-		for _, p := range s.Ports {
-			ports = append(ports, fmt.Sprintf("%s:%d->%d/%s", publicIps[task.NetworkInterface], p.Published, p.Target, p.Protocol))
+		for _, p := range service.Ports {
+			ports = append(ports, fmt.Sprintf("*:%d->%d/%s", p.Published, p.Target, p.Protocol))
 		}
-		tasks[i].Name = s.Name
-		tasks[i].Ports = ports
+		desc.Ports = ports
+		status = append(status, desc)
 	}
-	return tasks, nil
+	return status, nil
 }

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

@@ -104,7 +104,6 @@
           }
         },
         "SchedulingStrategy": "REPLICA",
-        "ServiceName": "simple",
         "ServiceRegistries": [
           {
             "RegistryArn": {

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

@@ -104,7 +104,6 @@
           }
         },
         "SchedulingStrategy": "REPLICA",
-        "ServiceName": "simple",
         "ServiceRegistries": [
           {
             "RegistryArn": {

+ 1 - 3
ecs/pkg/amazon/sdk/api.go

@@ -49,9 +49,7 @@ type secretsAPI interface {
 }
 
 type listAPI interface {
-	ListTasks(ctx context.Context, cluster string, name string) ([]string, error)
-	DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]compose.TaskStatus, error)
-	GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error)
+	DescribeService(ctx context.Context, cluster string, name string) (compose.ServiceStatus, error)
 }
 
 type waitAPI interface {

+ 39 - 75
ecs/pkg/amazon/sdk/api_mock.go

@@ -1,18 +1,16 @@
 // Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/docker/ecs-plugin/pkg/amazon (interfaces: API)
+// Source: github.com/docker/ecs-plugin/pkg/amazon/sdk (interfaces: API)
 
-// Package amazon is a generated GoMock package.
+// Package sdk is a generated GoMock package.
 package sdk
 
 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"
+	compose "github.com/docker/ecs-plugin/pkg/compose"
 	gomock "github.com/golang/mock/gomock"
+	reflect "reflect"
 )
 
 // MockAPI is a mock of API interface
@@ -124,6 +122,21 @@ func (mr *MockAPIMockRecorder) DeleteStack(arg0, arg1 interface{}) *gomock.Call
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStack", reflect.TypeOf((*MockAPI)(nil).DeleteStack), arg0, arg1)
 }
 
+// DescribeService mocks base method
+func (m *MockAPI) DescribeService(arg0 context.Context, arg1, arg2 string) (compose.ServiceStatus, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DescribeService", arg0, arg1, arg2)
+	ret0, _ := ret[0].(compose.ServiceStatus)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// DescribeService indicates an expected call of DescribeService
+func (mr *MockAPIMockRecorder) DescribeService(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeService", reflect.TypeOf((*MockAPI)(nil).DescribeService), arg0, arg1, arg2)
+}
+
 // DescribeStackEvents mocks base method
 func (m *MockAPI) DescribeStackEvents(arg0 context.Context, arg1 string) ([]*cloudformation.StackEvent, error) {
 	m.ctrl.T.Helper()
@@ -139,39 +152,34 @@ func (mr *MockAPIMockRecorder) DescribeStackEvents(arg0, arg1 interface{}) *gomo
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeStackEvents", reflect.TypeOf((*MockAPI)(nil).DescribeStackEvents), arg0, arg1)
 }
 
-// DescribeTasks mocks base method
-func (m *MockAPI) DescribeTasks(arg0 context.Context, arg1 string, arg2 ...string) ([]compose.TaskStatus, error) {
+// GetDefaultVPC mocks base method
+func (m *MockAPI) GetDefaultVPC(arg0 context.Context) (string, 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].([]compose.TaskStatus)
+	ret := m.ctrl.Call(m, "GetDefaultVPC", arg0)
+	ret0, _ := ret[0].(string)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// DescribeTasks indicates an expected call of DescribeTasks
-func (mr *MockAPIMockRecorder) DescribeTasks(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
+// GetDefaultVPC indicates an expected call of GetDefaultVPC
+func (mr *MockAPIMockRecorder) GetDefaultVPC(arg0 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	varargs := append([]interface{}{arg0, arg1}, arg2...)
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTasks", reflect.TypeOf((*MockAPI)(nil).DescribeTasks), varargs...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultVPC", reflect.TypeOf((*MockAPI)(nil).GetDefaultVPC), arg0)
 }
 
-// GetDefaultVPC mocks base method
-func (m *MockAPI) GetDefaultVPC(arg0 context.Context) (string, error) {
+// GetLoadBalancerARN mocks base method
+func (m *MockAPI) GetLoadBalancerARN(arg0 context.Context, arg1 string) (string, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetDefaultVPC", arg0)
+	ret := m.ctrl.Call(m, "GetLoadBalancerARN", arg0, arg1)
 	ret0, _ := ret[0].(string)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// GetDefaultVPC indicates an expected call of GetDefaultVPC
-func (mr *MockAPIMockRecorder) GetDefaultVPC(arg0 interface{}) *gomock.Call {
+// GetLoadBalancerARN indicates an expected call of GetLoadBalancerARN
+func (mr *MockAPIMockRecorder) GetLoadBalancerARN(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultVPC", reflect.TypeOf((*MockAPI)(nil).GetDefaultVPC), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLoadBalancerARN", reflect.TypeOf((*MockAPI)(nil).GetLoadBalancerARN), arg0, arg1)
 }
 
 // GetLogs mocks base method
@@ -188,26 +196,6 @@ func (mr *MockAPIMockRecorder) GetLogs(arg0, arg1, arg2 interface{}) *gomock.Cal
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogs", reflect.TypeOf((*MockAPI)(nil).GetLogs), arg0, arg1, arg2)
 }
 
-// GetPublicIPs mocks base method
-func (m *MockAPI) GetPublicIPs(arg0 context.Context, arg1 ...string) (map[string]string, error) {
-	m.ctrl.T.Helper()
-	varargs := []interface{}{arg0}
-	for _, a := range arg1 {
-		varargs = append(varargs, a)
-	}
-	ret := m.ctrl.Call(m, "GetPublicIPs", varargs...)
-	ret0, _ := ret[0].(map[string]string)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetPublicIPs indicates an expected call of GetPublicIPs
-func (mr *MockAPIMockRecorder) GetPublicIPs(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	varargs := append([]interface{}{arg0}, arg1...)
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPublicIPs", reflect.TypeOf((*MockAPI)(nil).GetPublicIPs), varargs...)
-}
-
 // GetStackID mocks base method
 func (m *MockAPI) GetStackID(arg0 context.Context, arg1 string) (string, error) {
 	m.ctrl.T.Helper()
@@ -268,19 +256,19 @@ func (mr *MockAPIMockRecorder) ListSecrets(arg0 interface{}) *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSecrets", reflect.TypeOf((*MockAPI)(nil).ListSecrets), arg0)
 }
 
-// ListTasks mocks base method
-func (m *MockAPI) ListTasks(arg0 context.Context, arg1, arg2 string) ([]string, error) {
+// LoadBalancerExists mocks base method
+func (m *MockAPI) LoadBalancerExists(arg0 context.Context, arg1 string) (bool, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "ListTasks", arg0, arg1, arg2)
-	ret0, _ := ret[0].([]string)
+	ret := m.ctrl.Call(m, "LoadBalancerExists", arg0, arg1)
+	ret0, _ := ret[0].(bool)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// ListTasks indicates an expected call of ListTasks
-func (mr *MockAPIMockRecorder) ListTasks(arg0, arg1, arg2 interface{}) *gomock.Call {
+// LoadBalancerExists indicates an expected call of LoadBalancerExists
+func (mr *MockAPIMockRecorder) LoadBalancerExists(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTasks", reflect.TypeOf((*MockAPI)(nil).ListTasks), arg0, arg1, arg2)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadBalancerExists", reflect.TypeOf((*MockAPI)(nil).LoadBalancerExists), arg0, arg1)
 }
 
 // StackExists mocks base method
@@ -326,27 +314,3 @@ func (mr *MockAPIMockRecorder) WaitStackComplete(arg0, arg1, arg2 interface{}) *
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitStackComplete", reflect.TypeOf((*MockAPI)(nil).WaitStackComplete), arg0, arg1, arg2)
 }
-
-// LoadBalancerExists mocks base method
-func (m *MockAPI) LoadBalancerExists(arg0 context.Context, arg1 string) (bool, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "LoadBalancerExists", arg0, arg1)
-	ret0, _ := ret[0].(bool)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// LoadBalancerExists indicates an expected call of VpcExists
-func (mr *MockAPIMockRecorder) LoadBalancerExists(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadBalancerExists", reflect.TypeOf((*MockAPI)(nil).LoadBalancerExists), arg0, arg1)
-}
-
-// GetLoadBalancerARN mocks base method
-func (m *MockAPI) GetLoadBalancerARN(arg0 context.Context, arg1 string) (string, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetLoadBalancerARN", arg0)
-	ret0, _ := ret[0].(string)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}

+ 19 - 32
ecs/pkg/amazon/sdk/sdk.go

@@ -341,48 +341,35 @@ func (s sdk) GetLogs(ctx context.Context, name string, consumer compose.LogConsu
 	}
 }
 
-func (s sdk) ListTasks(ctx context.Context, cluster string, service string) ([]string, error) {
-	tasks, err := s.ECS.ListTasksWithContext(ctx, &ecs.ListTasksInput{
-		Cluster:     aws.String(cluster),
-		ServiceName: aws.String(service),
+func (s sdk) DescribeService(ctx context.Context, cluster string, name string) (compose.ServiceStatus, error) {
+	services, err := s.ECS.DescribeServicesWithContext(ctx, &ecs.DescribeServicesInput{
+		Cluster:  aws.String(cluster),
+		Services: aws.StringSlice([]string{name}),
 	})
 	if err != nil {
-		return nil, err
-	}
-	arns := []string{}
-	for _, arn := range tasks.TaskArns {
-		arns = append(arns, *arn)
-	}
-	return arns, nil
+		return compose.ServiceStatus{}, err
+	}
+	return compose.ServiceStatus{
+		ID:       *services.Services[0].ServiceName,
+		Name:     name,
+		Replicas: int(*services.Services[0].RunningCount),
+		Desired:  int(*services.Services[0].DesiredCount),
+	}, nil
 }
 
-func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]compose.TaskStatus, error) {
-	tasks, err := s.ECS.DescribeTasksWithContext(ctx, &ecs.DescribeTasksInput{
+func (s sdk) ListTasks(ctx context.Context, cluster string, family string) ([]string, error) {
+	tasks, err := s.ECS.ListTasksWithContext(ctx, &ecs.ListTasksInput{
 		Cluster: aws.String(cluster),
-		Tasks:   aws.StringSlice(arns),
+		Family:  aws.String(family),
 	})
 	if err != nil {
 		return nil, err
 	}
-	result := []compose.TaskStatus{}
-	for _, task := range tasks.Tasks {
-		var networkInterface string
-		for _, attachement := range task.Attachments {
-			if *attachement.Type == "ElasticNetworkInterface" {
-				for _, pair := range attachement.Details {
-					if *pair.Name == "networkInterfaceId" {
-						networkInterface = *pair.Value
-					}
-				}
-			}
-		}
-		result = append(result, compose.TaskStatus{
-			State:            *task.LastStatus,
-			Service:          strings.Replace(*task.Group, "service:", "", 1),
-			NetworkInterface: networkInterface,
-		})
+	arns := []string{}
+	for _, arn := range tasks.TaskArns {
+		arns = append(arns, *arn)
 	}
-	return result, nil
+	return arns, nil
 }
 
 func (s sdk) GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error) {

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

@@ -14,7 +14,7 @@ type API interface {
 
 	Convert(project *types.Project) (*cloudformation.Template, error)
 	Logs(ctx context.Context, projectName string) error
-	Ps(background context.Context, project *types.Project) ([]TaskStatus, error)
+	Ps(background context.Context, project *types.Project) ([]ServiceStatus, error)
 
 	CreateSecret(ctx context.Context, secret Secret) (string, error)
 	InspectSecret(ctx context.Context, id string) (Secret, error)

+ 6 - 7
ecs/pkg/compose/types.go

@@ -2,13 +2,12 @@ package compose
 
 import "encoding/json"
 
-type TaskStatus struct {
-	Name             string
-	State            string
-	Service          string
-	NetworkInterface string
-	PublicIP         string
-	Ports            []string
+type ServiceStatus struct {
+	ID       string
+	Name     string
+	Replicas int
+	Desired  int
+	Ports    []string
 }
 
 const (

+ 6 - 5
ecs/tests/e2e_deploy_services_test.go

@@ -6,9 +6,10 @@ import (
 	"context"
 	"testing"
 
+	"github.com/docker/ecs-plugin/pkg/amazon/sdk"
+
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/session"
-	"github.com/docker/ecs-plugin/pkg/amazon"
 	"github.com/docker/ecs-plugin/pkg/docker"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/fs"
@@ -47,11 +48,11 @@ func composeUpSimpleService(t *testing.T, cmd icmd.Cmd, awsContext docker.AwsCon
 		},
 	})
 	assert.NilError(t, err)
-	sdk := amazon.NewAPI(session)
-	arns, err := sdk.ListTasks(bgContext, t.Name(), t.Name())
+	api := sdk.NewAPI(session)
+	arns, err := api.ListTasks(bgContext, t.Name(), t.Name())
 	assert.NilError(t, err)
-	tasks, err := sdk.DescribeTasks(bgContext, t.Name(), arns...)
-	publicIps, err := sdk.GetPublicIPs(context.Background(), tasks[0].NetworkInterface)
+	tasks, err := api.DescribeTasks(bgContext, t.Name(), arns...)
+	publicIps, err := api.GetPublicIPs(context.Background(), tasks[0].NetworkInterface)
 	assert.NilError(t, err)
 	for _, ip := range publicIps {
 		icmd.RunCommand("curl", "-I", "http://"+ip).Assert(t, icmd.Success)