ソースを参照

Merge pull request #1398 from aiordache/compose_top

Nicolas De loof 4 年 前
コミット
f08c58f903

+ 3 - 0
aci/compose.go

@@ -226,3 +226,6 @@ func (cs *aciComposeService) Remove(ctx context.Context, project *types.Project,
 func (cs *aciComposeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
 	return errdefs.ErrNotImplemented
 }
+func (cs *aciComposeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
+	return nil, errdefs.ErrNotImplemented
+}

+ 4 - 0
api/client/compose.go

@@ -99,3 +99,7 @@ func (c *composeService) Pause(ctx context.Context, project *types.Project) erro
 func (c *composeService) UnPause(ctx context.Context, project *types.Project) error {
 	return errdefs.ErrNotImplemented
 }
+
+func (c *composeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
+	return nil, errdefs.ErrNotImplemented
+}

+ 10 - 0
api/compose/api.go

@@ -63,6 +63,8 @@ type Service interface {
 	Pause(ctx context.Context, project *types.Project) error
 	// UnPause executes the equivalent to a `compose unpause`
 	UnPause(ctx context.Context, project *types.Project) error
+	// Top executes the equivalent to a `compose top`
+	Top(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
 }
 
 // BuildOptions group options of the Build API
@@ -220,6 +222,14 @@ type ContainerSummary struct {
 	Publishers []PortPublisher
 }
 
+// ContainerProcSummary holds container processes top data
+type ContainerProcSummary struct {
+	ID        string
+	Name      string
+	Processes [][]string
+	Titles    []string
+}
+
 // ServiceStatus hold status about a service
 type ServiceStatus struct {
 	ID         string

+ 1 - 0
cli/cmd/compose/compose.go

@@ -135,6 +135,7 @@ func Command(contextType string) *cobra.Command {
 		execCommand(&opts),
 		pauseCommand(&opts),
 		unpauseCommand(&opts),
+		topCommand(&opts),
 	)
 
 	if contextType == store.LocalContextType || contextType == store.DefaultContextType {

+ 95 - 0
cli/cmd/compose/top.go

@@ -0,0 +1,95 @@
+/*
+   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 (
+	"context"
+	"fmt"
+	"io"
+	"os"
+	"sort"
+	"strings"
+	"text/tabwriter"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/compose-cli/api/client"
+)
+
+type topOptions struct {
+	*projectOptions
+}
+
+func topCommand(p *projectOptions) *cobra.Command {
+	opts := topOptions{
+		projectOptions: p,
+	}
+	topCmd := &cobra.Command{
+		Use:   "top",
+		Short: "Display the running processes",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runTop(cmd.Context(), opts, args)
+		},
+	}
+	return topCmd
+}
+
+func runTop(ctx context.Context, opts topOptions, services []string) error {
+	c, err := client.NewWithDefaultLocalBackend(ctx)
+	if err != nil {
+		return err
+	}
+	projectName, err := opts.toProjectName()
+	if err != nil {
+		return err
+	}
+	containers, err := c.ComposeService().Top(ctx, projectName, services)
+	if err != nil {
+		return err
+	}
+
+	sort.Slice(containers, func(i, j int) bool {
+		return containers[i].Name < containers[j].Name
+	})
+
+	for _, container := range containers {
+		fmt.Printf("%s\n", container.Name)
+		err := psPrinter(os.Stdout, func(w io.Writer) {
+			for _, proc := range container.Processes {
+				info := []interface{}{}
+				for _, p := range proc {
+					info = append(info, p)
+				}
+				_, _ = fmt.Fprintf(w, strings.Repeat("%s\t", len(info))+"\n", info...)
+
+			}
+			fmt.Fprintln(w)
+		},
+			container.Titles...)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func psPrinter(out io.Writer, printer func(writer io.Writer), headers ...string) error {
+	w := tabwriter.NewWriter(out, 5, 1, 3, ' ', 0)
+	_, _ = fmt.Fprintln(w, strings.Join(headers, "\t"))
+	printer(w)
+	return w.Flush()
+}

+ 4 - 0
ecs/local/compose.go

@@ -191,3 +191,7 @@ func (e ecsLocalSimulation) Pause(ctx context.Context, project *types.Project) e
 func (e ecsLocalSimulation) UnPause(ctx context.Context, project *types.Project) error {
 	return e.compose.UnPause(ctx, project)
 }
+
+func (e ecsLocalSimulation) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
+	return e.compose.Top(ctx, projectName, services)
+}

+ 28 - 0
ecs/top.go

@@ -0,0 +1,28 @@
+/*
+   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 ecs
+
+import (
+	"context"
+
+	"github.com/docker/compose-cli/api/compose"
+	"github.com/docker/compose-cli/api/errdefs"
+)
+
+func (b *ecsAPIService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
+	return nil, errdefs.ErrNotImplemented
+}

+ 4 - 0
kube/compose.go

@@ -254,3 +254,7 @@ func (s *composeService) Pause(ctx context.Context, project *types.Project) erro
 func (s *composeService) UnPause(ctx context.Context, project *types.Project) error {
 	return errdefs.ErrNotImplemented
 }
+
+func (s *composeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
+	return nil, errdefs.ErrNotImplemented
+}

+ 59 - 0
local/compose/top.go

@@ -0,0 +1,59 @@
+/*
+   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 (
+	"context"
+
+	"github.com/docker/compose-cli/api/compose"
+	moby "github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	"golang.org/x/sync/errgroup"
+)
+
+func (s *composeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
+	var containers Containers
+	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
+		Filters: filters.NewArgs(projectFilter(projectName)),
+	})
+	if err != nil {
+		return nil, err
+	}
+	if len(services) > 0 {
+		containers = containers.filter(isService(services...))
+	}
+	summary := make([]compose.ContainerProcSummary, len(containers))
+	eg, ctx := errgroup.WithContext(ctx)
+	for i, c := range containers {
+		container := c
+		i := i
+		eg.Go(func() error {
+			topContent, err := s.apiClient.ContainerTop(ctx, container.ID, []string{})
+			if err != nil {
+				return err
+			}
+			summary[i] = compose.ContainerProcSummary{
+				ID:        container.ID,
+				Name:      getCanonicalContainerName(container),
+				Processes: topContent.Processes,
+				Titles:    topContent.Titles,
+			}
+			return nil
+		})
+	}
+	return summary, eg.Wait()
+}

+ 8 - 0
local/e2e/compose/compose_test.go

@@ -65,6 +65,14 @@ func TestLocalComposeUp(t *testing.T) {
 		res.Assert(t, icmd.Expected{Out: projectName + "_default"})
 	})
 
+	t.Run("top", func(t *testing.T) {
+		res := c.RunDockerCmd("compose", "-p", projectName, "top")
+		output := res.Stdout()
+		assert.Assert(t, strings.Contains(output, `UID    PID     PPID    C    STIME   TTY   TIME       CMD`))
+		assert.Assert(t, strings.Contains(output, `java -Xmx8m -Xms8m -jar /app/words.jar`))
+		assert.Assert(t, strings.Contains(output, `/dispatcher`))
+	})
+
 	t.Run("check compose labels", func(t *testing.T) {
 		wd, _ := os.Getwd()
 		res := c.RunDockerCmd("inspect", projectName+"_web_1")