diff --git a/aci/compose.go b/aci/compose.go index 93976e7ba..03bbbb895 100644 --- a/aci/compose.go +++ b/aci/compose.go @@ -29,6 +29,7 @@ import ( "github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/context/store" "github.com/docker/compose-cli/errdefs" + "github.com/docker/compose-cli/utils/formatter" ) type aciComposeService struct { @@ -119,7 +120,7 @@ func (cs *aciComposeService) Down(ctx context.Context, project string) error { return err } -func (cs *aciComposeService) Ps(ctx context.Context, project string) ([]compose.ServiceStatus, error) { +func (cs *aciComposeService) Ps(ctx context.Context, project string) ([]compose.ContainerSummary, error) { groupsClient, err := login.NewContainerGroupsClient(cs.ctx.SubscriptionID) if err != nil { return nil, err @@ -134,12 +135,30 @@ func (cs *aciComposeService) Ps(ctx context.Context, project string) ([]compose. return nil, fmt.Errorf("no containers found in ACI container group %s", project) } - res := []compose.ServiceStatus{} + res := []compose.ContainerSummary{} for _, container := range *group.Containers { if isContainerVisible(container, group, false) { continue } - res = append(res, convert.ContainerGroupToServiceStatus(getContainerID(group, container), group, container, cs.ctx.Location)) + var publishers []compose.PortPublisher + urls := formatter.PortsToStrings(convert.ToPorts(group.IPAddress, *container.Ports), convert.FQDN(group, cs.ctx.Location)) + for i, p := range *container.Ports { + publishers = append(publishers, compose.PortPublisher{ + URL: urls[i], + TargetPort: int(*p.Port), + PublishedPort: int(*p.Port), + Protocol: string(p.Protocol), + }) + } + id := getContainerID(group, container) + res = append(res, compose.ContainerSummary{ + ID: id, + Name: id, + Project: project, + Service: *container.Name, + State: convert.GetStatus(container, group), + Publishers: publishers, + }) } return res, nil } diff --git a/aci/convert/convert.go b/aci/convert/convert.go index 733547cad..a3f72b58f 100644 --- a/aci/convert/convert.go +++ b/aci/convert/convert.go @@ -314,13 +314,14 @@ func ContainerGroupToServiceStatus(containerID string, group containerinstance.C return compose.ServiceStatus{ ID: containerID, Name: *container.Name, - Ports: formatter.PortsToStrings(ToPorts(group.IPAddress, *container.Ports), fqdn(group, region)), + Ports: formatter.PortsToStrings(ToPorts(group.IPAddress, *container.Ports), FQDN(group, region)), Replicas: replicas, Desired: 1, } } -func fqdn(group containerinstance.ContainerGroup, region string) string { +// FQDN retrieve the fully qualified domain name for a ContainerGroup +func FQDN(group containerinstance.ContainerGroup, region string) string { fqdn := "" if group.IPAddress != nil && group.IPAddress.DNSNameLabel != nil && *group.IPAddress.DNSNameLabel != "" { fqdn = *group.IPAddress.DNSNameLabel + "." + region + ".azurecontainer.io" @@ -348,7 +349,7 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe hostConfig := ToHostConfig(cc, cg) config := &containers.RuntimeConfig{ - FQDN: fqdn(cg, region), + FQDN: FQDN(cg, region), Env: envVars, } diff --git a/api/client/compose.go b/api/client/compose.go index 3503e83a4..e139e20f2 100644 --- a/api/client/compose.go +++ b/api/client/compose.go @@ -60,7 +60,7 @@ func (c *composeService) Logs(context.Context, string, compose.LogConsumer) erro return errdefs.ErrNotImplemented } -func (c *composeService) Ps(context.Context, string) ([]compose.ServiceStatus, error) { +func (c *composeService) Ps(context.Context, string) ([]compose.ContainerSummary, error) { return nil, errdefs.ErrNotImplemented } diff --git a/api/compose/api.go b/api/compose/api.go index 95cbb5b98..e1badf3da 100644 --- a/api/compose/api.go +++ b/api/compose/api.go @@ -41,7 +41,7 @@ type Service interface { // Logs executes the equivalent to a `compose logs` Logs(ctx context.Context, projectName string, consumer LogConsumer) error // Ps executes the equivalent to a `compose ps` - Ps(ctx context.Context, projectName string) ([]ServiceStatus, error) + Ps(ctx context.Context, projectName string) ([]ContainerSummary, error) // List executes the equivalent to a `docker stack ls` List(ctx context.Context, projectName string) ([]Stack, error) // Convert translate compose model into backend's native format @@ -56,6 +56,16 @@ type PortPublisher struct { Protocol string } +// ContainerSummary hold high-level description of a container +type ContainerSummary struct { + ID string + Name string + Project string + Service string + State string + Publishers []PortPublisher +} + // ServiceStatus hold status about a service type ServiceStatus struct { ID string diff --git a/cli/cmd/compose/ps.go b/cli/cmd/compose/ps.go index 235443d0d..b7b5ffed7 100644 --- a/cli/cmd/compose/ps.go +++ b/cli/cmd/compose/ps.go @@ -21,12 +21,12 @@ import ( "fmt" "io" "os" + "sort" "strings" "github.com/spf13/cobra" "github.com/docker/compose-cli/api/client" - "github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/formatter" ) @@ -54,44 +54,34 @@ func runPs(ctx context.Context, opts composeOptions) error { if err != nil { return err } - serviceList, err := c.ComposeService().Ps(ctx, projectName) + containers, err := c.ComposeService().Ps(ctx, projectName) if err != nil { return err } if opts.Quiet { - for _, s := range serviceList { + for _, s := range containers { fmt.Println(s.ID) } return nil } - view := viewFromServiceStatusList(serviceList) - return formatter.Print(view, opts.Format, os.Stdout, + + sort.Slice(containers, func(i, j int) bool { + return containers[i].Name < containers[j].Name + }) + + return formatter.Print(containers, opts.Format, os.Stdout, func(w io.Writer) { - for _, service := range view { - _, _ = fmt.Fprintf(w, "%s\t%s\t%d/%d\t%s\n", service.ID, service.Name, service.Replicas, service.Desired, strings.Join(service.Ports, ", ")) + for _, container := range containers { + var ports []string + for _, p := range container.Publishers { + if p.URL == "" { + ports = append(ports, fmt.Sprintf("%d/%s", p.TargetPort, p.Protocol)) + } else { + ports = append(ports, fmt.Sprintf("%s->%d/%s", p.URL, p.TargetPort, p.Protocol)) + } + } + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", container.Name, container.Service, container.State, strings.Join(ports, ", ")) } }, - "ID", "NAME", "REPLICAS", "PORTS") -} - -type serviceStatusView struct { - ID string - Name string - Replicas int - Desired int - Ports []string -} - -func viewFromServiceStatusList(serviceStatusList []compose.ServiceStatus) []serviceStatusView { - retList := make([]serviceStatusView, len(serviceStatusList)) - for i, s := range serviceStatusList { - retList[i] = serviceStatusView{ - ID: s.ID, - Name: s.Name, - Replicas: s.Replicas, - Desired: s.Desired, - Ports: s.Ports, - } - } - return retList + "NAME", "SERVICE", "STATE", "PORTS") } diff --git a/ecs/aws.go b/ecs/aws.go index 782c25c8b..5cf50fbfa 100644 --- a/ecs/aws.go +++ b/ecs/aws.go @@ -63,6 +63,7 @@ type API interface { DeleteSecret(ctx context.Context, id string, recover bool) error GetLogs(ctx context.Context, name string, consumer func(service, container, message string)) error DescribeService(ctx context.Context, cluster string, arn string) (compose.ServiceStatus, error) + DescribeServiceTasks(ctx context.Context, cluster string, project string, service string) ([]compose.ContainerSummary, error) getURLWithPortMapping(ctx context.Context, targetGroupArns []string) ([]compose.PortPublisher, error) ListTasks(ctx context.Context, cluster string, family string) ([]string, error) GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error) diff --git a/ecs/aws_mock.go b/ecs/aws_mock.go index 428bc8c80..9d438bf29 100644 --- a/ecs/aws_mock.go +++ b/ecs/aws_mock.go @@ -224,6 +224,21 @@ func (mr *MockAPIMockRecorder) DescribeService(arg0, arg1, arg2 interface{}) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeService", reflect.TypeOf((*MockAPI)(nil).DescribeService), arg0, arg1, arg2) } +// DescribeServiceTasks mocks base method +func (m *MockAPI) DescribeServiceTasks(arg0 context.Context, arg1, arg2, arg3 string) ([]compose.ContainerSummary, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DescribeServiceTasks", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].([]compose.ContainerSummary) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeServiceTasks indicates an expected call of DescribeServiceTasks +func (mr *MockAPIMockRecorder) DescribeServiceTasks(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeServiceTasks", reflect.TypeOf((*MockAPI)(nil).DescribeServiceTasks), arg0, arg1, arg2, arg3) +} + // DescribeStackEvents mocks base method func (m *MockAPI) DescribeStackEvents(arg0 context.Context, arg1 string) ([]*cloudformation.StackEvent, error) { m.ctrl.T.Helper() diff --git a/ecs/local/compose.go b/ecs/local/compose.go index f1ad7b58d..9bf64a5f9 100644 --- a/ecs/local/compose.go +++ b/ecs/local/compose.go @@ -207,7 +207,7 @@ func (e ecsLocalSimulation) Logs(ctx context.Context, projectName string, consum return cmd.Run() } -func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string) ([]compose.ServiceStatus, error) { +func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) { return nil, errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose ps") } func (e ecsLocalSimulation) List(ctx context.Context, projectName string) ([]compose.Stack, error) { diff --git a/ecs/ps.go b/ecs/ps.go index e283668fc..a1c4ea7b6 100644 --- a/ecs/ps.go +++ b/ecs/ps.go @@ -18,13 +18,11 @@ package ecs import ( "context" - "fmt" - "strings" "github.com/docker/compose-cli/api/compose" ) -func (b *ecsAPIService) Ps(ctx context.Context, project string) ([]compose.ServiceStatus, error) { +func (b *ecsAPIService) Ps(ctx context.Context, project string) ([]compose.ContainerSummary, error) { cluster, err := b.aws.GetStackClusterID(ctx, project) if err != nil { return nil, err @@ -38,23 +36,23 @@ func (b *ecsAPIService) Ps(ctx context.Context, project string) ([]compose.Servi return nil, nil } - status := []compose.ServiceStatus{} + summary := []compose.ContainerSummary{} for _, arn := range servicesARN { - state, err := b.aws.DescribeService(ctx, cluster, arn) + service, err := b.aws.DescribeService(ctx, cluster, arn) if err != nil { return nil, err } - ports := []string{} - for _, lb := range state.Publishers { - ports = append(ports, fmt.Sprintf( - "%s:%d->%d/%s", - lb.URL, - lb.PublishedPort, - lb.TargetPort, - strings.ToLower(lb.Protocol))) + + tasks, err := b.aws.DescribeServiceTasks(ctx, cluster, project, service.Name) + if err != nil { + return nil, err } - state.Ports = ports - status = append(status, state) + + for i, t := range tasks { + t.Publishers = service.Publishers + tasks[i] = t + } + summary = append(summary, tasks...) } - return status, nil + return summary, nil } diff --git a/ecs/sdk.go b/ecs/sdk.go index d12e61710..b44ecbfab 100644 --- a/ecs/sdk.go +++ b/ecs/sdk.go @@ -819,6 +819,69 @@ func (s sdk) DescribeService(ctx context.Context, cluster string, arn string) (c }, nil } +func (s sdk) DescribeServiceTasks(ctx context.Context, cluster string, project string, service string) ([]compose.ContainerSummary, error) { + var summary []compose.ContainerSummary + familly := fmt.Sprintf("%s-%s", project, service) + var token *string + for { + list, err := s.ECS.ListTasks(&ecs.ListTasksInput{ + Cluster: aws.String(cluster), + Family: aws.String(familly), + LaunchType: nil, + MaxResults: nil, + NextToken: token, + }) + if err != nil { + return nil, err + } + + if len(list.TaskArns) == 0 { + break + } + tasks, err := s.ECS.DescribeTasksWithContext(ctx, &ecs.DescribeTasksInput{ + Cluster: aws.String(cluster), + Include: aws.StringSlice([]string{"TAGS"}), + Tasks: list.TaskArns, + }) + if err != nil { + return nil, err + } + + for _, t := range tasks.Tasks { + var project string + var service string + for _, tag := range t.Tags { + switch aws.StringValue(tag.Key) { + case compose.ProjectTag: + project = aws.StringValue(tag.Value) + case compose.ServiceTag: + service = aws.StringValue(tag.Value) + } + } + + id, err := arn.Parse(aws.StringValue(t.TaskArn)) + if err != nil { + return nil, err + } + + summary = append(summary, compose.ContainerSummary{ + ID: id.String(), + Name: id.Resource, + Project: project, + Service: service, + State: strings.Title(strings.ToLower(aws.StringValue(t.LastStatus))), + }) + } + + if list.NextToken == token { + break + } + token = list.NextToken + } + + return summary, nil +} + func (s sdk) getURLWithPortMapping(ctx context.Context, targetGroupArns []string) ([]compose.PortPublisher, error) { if len(targetGroupArns) == 0 { return nil, nil @@ -861,10 +924,10 @@ func (s sdk) getURLWithPortMapping(ctx context.Context, targetGroupArns []string continue } loadBalancers = append(loadBalancers, compose.PortPublisher{ - URL: aws.StringValue(lb.DNSName), + URL: fmt.Sprintf("%s:%d", aws.StringValue(lb.DNSName), aws.Int64Value(tg.Port)), TargetPort: int(aws.Int64Value(tg.Port)), PublishedPort: int(aws.Int64Value(tg.Port)), - Protocol: aws.StringValue(tg.Protocol), + Protocol: strings.ToLower(aws.StringValue(tg.Protocol)), }) } diff --git a/example/backend.go b/example/backend.go index 540849505..23274f11d 100644 --- a/example/backend.go +++ b/example/backend.go @@ -169,7 +169,7 @@ func (cs *composeService) Down(ctx context.Context, project string) error { return nil } -func (cs *composeService) Ps(ctx context.Context, project string) ([]compose.ServiceStatus, error) { +func (cs *composeService) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) { return nil, errdefs.ErrNotImplemented } func (cs *composeService) List(ctx context.Context, project string) ([]compose.Stack, error) { diff --git a/local/compose/compose_test.go b/local/compose/compose_test.go deleted file mode 100644 index 6aa295558..000000000 --- a/local/compose/compose_test.go +++ /dev/null @@ -1,143 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "path/filepath" - "testing" - - composetypes "github.com/compose-spec/compose-go/types" - "github.com/docker/docker/api/types" - mountTypes "github.com/docker/docker/api/types/mount" - "gotest.tools/v3/assert" - - "github.com/docker/compose-cli/api/compose" -) - -func TestContainersToStacks(t *testing.T) { - containers := []types.Container{ - { - ID: "service1", - State: "running", - Labels: map[string]string{projectLabel: "project1"}, - }, - { - ID: "service2", - State: "running", - Labels: map[string]string{projectLabel: "project1"}, - }, - { - ID: "service3", - State: "running", - Labels: map[string]string{projectLabel: "project2"}, - }, - } - stacks, err := containersToStacks(containers) - assert.NilError(t, err) - assert.DeepEqual(t, stacks, []compose.Stack{ - { - ID: "project1", - Name: "project1", - Status: "running(2)", - }, - { - ID: "project2", - Name: "project2", - Status: "running(1)", - }, - }) -} - -func TestContainersToServiceStatus(t *testing.T) { - containers := []types.Container{ - { - ID: "c1", - State: "running", - Labels: map[string]string{serviceLabel: "service1"}, - }, - { - ID: "c2", - State: "exited", - Labels: map[string]string{serviceLabel: "service1"}, - }, - { - ID: "c3", - State: "running", - Labels: map[string]string{serviceLabel: "service1"}, - }, - { - ID: "c4", - State: "running", - Labels: map[string]string{serviceLabel: "service2"}, - }, - } - services, err := containersToServiceStatus(containers) - assert.NilError(t, err) - assert.DeepEqual(t, services, []compose.ServiceStatus{ - { - ID: "service1", - Name: "service1", - Replicas: 2, - Desired: 3, - }, - { - ID: "service2", - Name: "service2", - Replicas: 1, - Desired: 1, - }, - }) -} - -func TestStacksMixedStatus(t *testing.T) { - assert.Equal(t, combinedStatus([]string{"running"}), "running(1)") - assert.Equal(t, combinedStatus([]string{"running", "running", "running"}), "running(3)") - assert.Equal(t, combinedStatus([]string{"running", "exited", "running"}), "exited(1), running(2)") -} - -func TestBuildBindMount(t *testing.T) { - project := composetypes.Project{} - volume := composetypes.ServiceVolumeConfig{ - Type: composetypes.VolumeTypeBind, - Source: "e2e/volume-test", - Target: "/data", - } - mount, err := buildMount(project, volume) - assert.NilError(t, err) - assert.Assert(t, filepath.IsAbs(mount.Source)) - assert.Equal(t, mount.Type, mountTypes.TypeBind) -} - -func TestBuildVolumeMount(t *testing.T) { - project := composetypes.Project{ - Name: "myProject", - Volumes: composetypes.Volumes(map[string]composetypes.VolumeConfig{ - "myVolume": { - Name: "myProject_myVolume", - }, - }), - } - volume := composetypes.ServiceVolumeConfig{ - Type: composetypes.VolumeTypeVolume, - Source: "myVolume", - Target: "/data", - } - mount, err := buildMount(project, volume) - assert.NilError(t, err) - assert.Equal(t, mount.Source, "myProject_myVolume") - assert.Equal(t, mount.Type, mountTypes.TypeVolume) -} diff --git a/local/compose/create_test.go b/local/compose/create_test.go new file mode 100644 index 000000000..fa1b3681e --- /dev/null +++ b/local/compose/create_test.go @@ -0,0 +1,62 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package compose + +import ( + "os" + "path/filepath" + "testing" + + composetypes "github.com/compose-spec/compose-go/types" + mountTypes "github.com/docker/docker/api/types/mount" + "gotest.tools/v3/assert" +) + +func TestBuildBindMount(t *testing.T) { + project := composetypes.Project{} + volume := composetypes.ServiceVolumeConfig{ + Type: composetypes.VolumeTypeBind, + Source: "", + Target: "/data", + } + mount, err := buildMount(project, volume) + assert.NilError(t, err) + assert.Assert(t, filepath.IsAbs(mount.Source)) + _, err = os.Stat(mount.Source) + assert.NilError(t, err) + assert.Equal(t, mount.Type, mountTypes.TypeBind) +} + +func TestBuildVolumeMount(t *testing.T) { + project := composetypes.Project{ + Name: "myProject", + Volumes: composetypes.Volumes(map[string]composetypes.VolumeConfig{ + "myVolume": { + Name: "myProject_myVolume", + }, + }), + } + volume := composetypes.ServiceVolumeConfig{ + Type: composetypes.VolumeTypeVolume, + Source: "myVolume", + Target: "/data", + } + mount, err := buildMount(project, volume) + assert.NilError(t, err) + assert.Equal(t, mount.Source, "myProject_myVolume") + assert.Equal(t, mount.Type, mountTypes.TypeVolume) +} diff --git a/local/compose/ls_test.go b/local/compose/ls_test.go new file mode 100644 index 000000000..9d9ea9e95 --- /dev/null +++ b/local/compose/ls_test.go @@ -0,0 +1,66 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package compose + +import ( + "testing" + + "github.com/docker/compose-cli/api/compose" + + moby "github.com/docker/docker/api/types" + "gotest.tools/v3/assert" +) + +func TestContainersToStacks(t *testing.T) { + containers := []moby.Container{ + { + ID: "service1", + State: "running", + Labels: map[string]string{projectLabel: "project1"}, + }, + { + ID: "service2", + State: "running", + Labels: map[string]string{projectLabel: "project1"}, + }, + { + ID: "service3", + State: "running", + Labels: map[string]string{projectLabel: "project2"}, + }, + } + stacks, err := containersToStacks(containers) + assert.NilError(t, err) + assert.DeepEqual(t, stacks, []compose.Stack{ + { + ID: "project1", + Name: "project1", + Status: "running(2)", + }, + { + ID: "project2", + Name: "project2", + Status: "running(1)", + }, + }) +} + +func TestStacksMixedStatus(t *testing.T) { + assert.Equal(t, combinedStatus([]string{"running"}), "running(1)") + assert.Equal(t, combinedStatus([]string{"running", "running", "running"}), "running(3)") + assert.Equal(t, combinedStatus([]string{"running", "exited", "running"}), "exited(1), running(2)") +} diff --git a/local/compose/ps.go b/local/compose/ps.go index 39b1ecfaf..7c6ca1b5e 100644 --- a/local/compose/ps.go +++ b/local/compose/ps.go @@ -21,15 +21,13 @@ import ( "fmt" "sort" - convert "github.com/docker/compose-cli/local/moby" - "github.com/docker/compose-cli/api/compose" moby "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" ) -func (s *composeService) Ps(ctx context.Context, projectName string) ([]compose.ServiceStatus, error) { - list, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ +func (s *composeService) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) { + containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ Filters: filters.NewArgs( projectFilter(projectName), ), @@ -37,31 +35,33 @@ func (s *composeService) Ps(ctx context.Context, projectName string) ([]compose. if err != nil { return nil, err } - return containersToServiceStatus(list) -} -func containersToServiceStatus(containers []moby.Container) ([]compose.ServiceStatus, error) { - containersByLabel, keys, err := groupContainerByLabel(containers, serviceLabel) - if err != nil { - return nil, err - } - var services []compose.ServiceStatus - for _, service := range keys { - containers := containersByLabel[service] - runnningContainers := []moby.Container{} - for _, container := range containers { - if container.State == convert.ContainerRunning { - runnningContainers = append(runnningContainers, container) + var summary []compose.ContainerSummary + for _, c := range containers { + var publishers []compose.PortPublisher + for _, p := range c.Ports { + var url string + if p.PublicPort != 0 { + url = fmt.Sprintf("%s:%d", p.IP, p.PublicPort) } + publishers = append(publishers, compose.PortPublisher{ + URL: url, + TargetPort: int(p.PrivatePort), + PublishedPort: int(p.PublicPort), + Protocol: p.Type, + }) } - services = append(services, compose.ServiceStatus{ - ID: service, - Name: service, - Desired: len(containers), - Replicas: len(runnningContainers), + + summary = append(summary, compose.ContainerSummary{ + ID: c.ID, + Name: getContainerName(c), + Project: c.Labels[projectLabel], + Service: c.Labels[serviceLabel], + State: c.State, + Publishers: publishers, }) } - return services, nil + return summary, nil } func groupContainerByLabel(containers []moby.Container, labelName string) (map[string][]moby.Container, []string, error) { diff --git a/server/proxy/compose.go b/server/proxy/compose.go index 59afcf556..3c584d201 100644 --- a/server/proxy/compose.go +++ b/server/proxy/compose.go @@ -54,11 +54,12 @@ func (p *proxy) Services(ctx context.Context, request *composev1.ComposeServices } projectName = project.Name } - services, err := Client(ctx).ComposeService().Ps(ctx, projectName) + response := []*composev1.Service{} + _, err := Client(ctx).ComposeService().Ps(ctx, projectName) if err != nil { return nil, err } - response := []*composev1.Service{} + /* FIXME need to create `docker service ls` command to re-introduce this feature for _, service := range services { response = append(response, &composev1.Service{ Id: service.ID, @@ -67,7 +68,7 @@ func (p *proxy) Services(ctx context.Context, request *composev1.ComposeServices Desired: uint32(service.Desired), Ports: service.Ports, }) - } + }*/ return &composev1.ComposeServicesResponse{Services: response}, nil } diff --git a/tests/aci-e2e/e2e-aci_test.go b/tests/aci-e2e/e2e-aci_test.go index ccc950ee1..edc2b3cef 100644 --- a/tests/aci-e2e/e2e-aci_test.go +++ b/tests/aci-e2e/e2e-aci_test.go @@ -692,7 +692,7 @@ func TestUpUpdate(t *testing.T) { for _, l := range out { if strings.Contains(l, serverContainer) { webRunning = true - strings.Contains(l, ":80->80/tcp") + assert.Check(t, strings.Contains(l, ":80->80/tcp")) } } assert.Assert(t, webRunning, "web container not running ; ps:\n"+res.Stdout()) @@ -734,20 +734,23 @@ func TestUpUpdate(t *testing.T) { var wordsDisplayed, webDisplayed, dbDisplayed bool for _, line := range l { fields := strings.Fields(line) - containerID := fields[0] - switch containerID { + name := fields[0] + switch name { case wordsContainer: wordsDisplayed = true - assert.DeepEqual(t, fields, []string{containerID, "words", "1/1"}) + assert.Equal(t, fields[2], "Running") case dbContainer: dbDisplayed = true - assert.DeepEqual(t, fields, []string{containerID, "db", "1/1"}) + assert.Equal(t, fields[2], "Running") case serverContainer: webDisplayed = true - assert.Equal(t, fields[1], "web") + assert.Equal(t, fields[2], "Running") assert.Check(t, strings.Contains(fields[3], ":80->80/tcp")) } } + assert.Check(t, webDisplayed, "webDisplayed"+res.Stdout()) + assert.Check(t, wordsDisplayed, "wordsDisplayed"+res.Stdout()) + assert.Check(t, dbDisplayed, "dbDisplayed"+res.Stdout()) assert.Check(t, webDisplayed && wordsDisplayed && dbDisplayed, "\n%s\n", res.Stdout()) }) diff --git a/tests/ecs-e2e/e2e-ecs_test.go b/tests/ecs-e2e/e2e-ecs_test.go index 6c04823f9..9a8e8880a 100644 --- a/tests/ecs-e2e/e2e-ecs_test.go +++ b/tests/ecs-e2e/e2e-ecs_test.go @@ -100,7 +100,7 @@ func TestCompose(t *testing.T) { switch serviceName { case "db": dbDisplayed = true - assert.DeepEqual(t, fields, []string{containerID, serviceName, "1/1"}) + assert.DeepEqual(t, fields, []string{containerID, serviceName, "Running"}) case "words": wordsDisplayed = true assert.Check(t, strings.Contains(fields[3], ":8080->8080/tcp"))