浏览代码

Add `compose top` command

Signed-off-by: aiordache <[email protected]>
aiordache 4 年之前
父节点
当前提交
79af862613
共有 9 个文件被更改,包括 217 次插入0 次删除
  1. 3 0
      aci/compose.go
  2. 4 0
      api/client/compose.go
  3. 10 0
      api/compose/api.go
  4. 1 0
      cli/cmd/compose/compose.go
  5. 95 0
      cli/cmd/compose/top.go
  6. 4 0
      ecs/local/compose.go
  7. 28 0
      ecs/top.go
  8. 4 0
      kube/compose.go
  9. 68 0
      local/compose/top.go

+ 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
@@ -214,6 +216,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
+}

+ 68 - 0
local/compose/top.go

@@ -0,0 +1,68 @@
+/*
+   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) {
+	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
+		Filters: filters.NewArgs(projectFilter(projectName)),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	ignore := func(string) bool {
+		return false
+	}
+	if len(services) > 0 {
+		ignore = func(s string) bool {
+			return !contains(services, s)
+		}
+	}
+	summary := make([]compose.ContainerProcSummary, len(containers))
+	eg, ctx := errgroup.WithContext(ctx)
+	for i, c := range containers {
+		container := c
+		service := c.Labels[serviceLabel]
+		if ignore(service) {
+			continue
+		}
+		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()
+}