mirror of https://github.com/docker/compose.git
Add `compose top` command
Signed-off-by: aiordache <anca.iordache@docker.com>
This commit is contained in:
parent
b3025ca4fe
commit
79af862613
|
@ -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 {
|
func (cs *aciComposeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
|
||||||
return errdefs.ErrNotImplemented
|
return errdefs.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
func (cs *aciComposeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
|
||||||
|
return nil, errdefs.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
func (c *composeService) UnPause(ctx context.Context, project *types.Project) error {
|
||||||
return errdefs.ErrNotImplemented
|
return errdefs.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *composeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
|
||||||
|
return nil, errdefs.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ type Service interface {
|
||||||
Pause(ctx context.Context, project *types.Project) error
|
Pause(ctx context.Context, project *types.Project) error
|
||||||
// UnPause executes the equivalent to a `compose unpause`
|
// UnPause executes the equivalent to a `compose unpause`
|
||||||
UnPause(ctx context.Context, project *types.Project) error
|
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
|
// BuildOptions group options of the Build API
|
||||||
|
@ -214,6 +216,14 @@ type ContainerSummary struct {
|
||||||
Publishers []PortPublisher
|
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
|
// ServiceStatus hold status about a service
|
||||||
type ServiceStatus struct {
|
type ServiceStatus struct {
|
||||||
ID string
|
ID string
|
||||||
|
|
|
@ -135,6 +135,7 @@ func Command(contextType string) *cobra.Command {
|
||||||
execCommand(&opts),
|
execCommand(&opts),
|
||||||
pauseCommand(&opts),
|
pauseCommand(&opts),
|
||||||
unpauseCommand(&opts),
|
unpauseCommand(&opts),
|
||||||
|
topCommand(&opts),
|
||||||
)
|
)
|
||||||
|
|
||||||
if contextType == store.LocalContextType || contextType == store.DefaultContextType {
|
if contextType == store.LocalContextType || contextType == store.DefaultContextType {
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
|
@ -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 {
|
func (e ecsLocalSimulation) UnPause(ctx context.Context, project *types.Project) error {
|
||||||
return e.compose.UnPause(ctx, project)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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 {
|
func (s *composeService) UnPause(ctx context.Context, project *types.Project) error {
|
||||||
return errdefs.ErrNotImplemented
|
return errdefs.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *composeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
|
||||||
|
return nil, errdefs.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
Loading…
Reference in New Issue