Merge pull request #1172 from docker/ls_filters

introduce `--filter` option on `compose ls`
This commit is contained in:
Nicolas De loof 2021-01-22 10:54:20 +01:00 committed by GitHub
commit dc790d542f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 48 additions and 30 deletions

View File

@ -163,20 +163,17 @@ func (cs *aciComposeService) Ps(ctx context.Context, project string) ([]compose.
return res, nil return res, nil
} }
func (cs *aciComposeService) List(ctx context.Context, project string) ([]compose.Stack, error) { func (cs *aciComposeService) List(ctx context.Context) ([]compose.Stack, error) {
containerGroups, err := getACIContainerGroups(ctx, cs.ctx.SubscriptionID, cs.ctx.ResourceGroup) containerGroups, err := getACIContainerGroups(ctx, cs.ctx.SubscriptionID, cs.ctx.ResourceGroup)
if err != nil { if err != nil {
return nil, err return nil, err
} }
stacks := []compose.Stack{} var stacks []compose.Stack
for _, group := range containerGroups { for _, group := range containerGroups {
if _, found := group.Tags[composeContainerTag]; !found { if _, found := group.Tags[composeContainerTag]; !found {
continue continue
} }
if project != "" && *group.Name != project {
continue
}
state := compose.RUNNING state := compose.RUNNING
for _, container := range *group.ContainerGroupProperties.Containers { for _, container := range *group.ContainerGroupProperties.Containers {
containerState := convert.GetStatus(container, group) containerState := convert.GetStatus(container, group)

View File

@ -64,7 +64,7 @@ func (c *composeService) Ps(context.Context, string) ([]compose.ContainerSummary
return nil, errdefs.ErrNotImplemented return nil, errdefs.ErrNotImplemented
} }
func (c *composeService) List(context.Context, string) ([]compose.Stack, error) { func (c *composeService) List(context.Context) ([]compose.Stack, error) {
return nil, errdefs.ErrNotImplemented return nil, errdefs.ErrNotImplemented
} }

View File

@ -44,7 +44,7 @@ type Service interface {
// Ps executes the equivalent to a `compose ps` // Ps executes the equivalent to a `compose ps`
Ps(ctx context.Context, projectName string) ([]ContainerSummary, error) Ps(ctx context.Context, projectName string) ([]ContainerSummary, error)
// List executes the equivalent to a `docker stack ls` // List executes the equivalent to a `docker stack ls`
List(ctx context.Context, projectName string) ([]Stack, error) List(ctx context.Context) ([]Stack, error)
// Convert translate compose model into backend's native format // Convert translate compose model into backend's native format
Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error) Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
// RunOneOffContainer creates a service oneoff container and starts its dependencies // RunOneOffContainer creates a service oneoff container and starts its dependencies

View File

@ -23,6 +23,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/docker/cli/opts"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/client"
@ -33,10 +34,11 @@ import (
type lsOptions struct { type lsOptions struct {
Format string Format string
Quiet bool Quiet bool
Filter opts.FilterOpt
} }
func listCommand() *cobra.Command { func listCommand() *cobra.Command {
opts := lsOptions{} opts := lsOptions{Filter: opts.NewFilterOpt()}
lsCmd := &cobra.Command{ lsCmd := &cobra.Command{
Use: "ls", Use: "ls",
Short: "List running compose projects", Short: "List running compose projects",
@ -45,16 +47,28 @@ func listCommand() *cobra.Command {
}, },
} }
lsCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].") lsCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].")
lsCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") lsCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs.")
lsCmd.Flags().Var(&opts.Filter, "filter", "Filter output based on conditions provided.")
return lsCmd return lsCmd
} }
var acceptedListFilters = map[string]bool{
"name": true,
}
func runList(ctx context.Context, opts lsOptions) error { func runList(ctx context.Context, opts lsOptions) error {
filters := opts.Filter.Value()
err := filters.Validate(acceptedListFilters)
if err != nil {
return err
}
c, err := client.NewWithDefaultLocalBackend(ctx) c, err := client.NewWithDefaultLocalBackend(ctx)
if err != nil { if err != nil {
return err return err
} }
stackList, err := c.ComposeService().List(ctx, "") stackList, err := c.ComposeService().List(ctx)
if err != nil { if err != nil {
return err return err
} }
@ -64,6 +78,18 @@ func runList(ctx context.Context, opts lsOptions) error {
} }
return nil return nil
} }
if filters.Len() > 0 {
var filtered []compose.Stack
for _, s := range stackList {
if filters.Contains("name") && !filters.Match("name", s.Name) {
continue
}
filtered = append(filtered, s)
}
stackList = filtered
}
view := viewFromStackList(stackList) view := viewFromStackList(stackList)
return formatter.Print(view, opts.Format, os.Stdout, func(w io.Writer) { return formatter.Print(view, opts.Format, os.Stdout, func(w io.Writer) {
for _, stack := range view { for _, stack := range view {

View File

@ -77,7 +77,7 @@ func (p *proxy) Services(ctx context.Context, request *composev1.ComposeServices
} }
func (p *proxy) Stacks(ctx context.Context, request *composev1.ComposeStacksRequest) (*composev1.ComposeStacksResponse, error) { func (p *proxy) Stacks(ctx context.Context, request *composev1.ComposeStacksRequest) (*composev1.ComposeStacksResponse, error) {
stacks, err := Client(ctx).ComposeService().List(ctx, request.ProjectName) stacks, err := Client(ctx).ComposeService().List(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -49,7 +49,7 @@ type API interface {
UpdateStack(ctx context.Context, changeset string) error UpdateStack(ctx context.Context, changeset string) error
WaitStackComplete(ctx context.Context, name string, operation int) error WaitStackComplete(ctx context.Context, name string, operation int) error
GetStackID(ctx context.Context, name string) (string, error) GetStackID(ctx context.Context, name string) (string, error)
ListStacks(ctx context.Context, name string) ([]compose.Stack, error) ListStacks(ctx context.Context) ([]compose.Stack, error)
GetStackClusterID(ctx context.Context, stack string) (string, error) GetStackClusterID(ctx context.Context, stack string) (string, error)
GetServiceTaskDefinition(ctx context.Context, cluster string, serviceArns []string) (map[string]string, error) GetServiceTaskDefinition(ctx context.Context, cluster string, serviceArns []string) (map[string]string, error)
ListStackServices(ctx context.Context, stack string) ([]string, error) ListStackServices(ctx context.Context, stack string) ([]string, error)

View File

@ -6,14 +6,12 @@ package ecs
import ( import (
context "context" context "context"
reflect "reflect"
cloudformation "github.com/aws/aws-sdk-go/service/cloudformation" cloudformation "github.com/aws/aws-sdk-go/service/cloudformation"
ecs "github.com/aws/aws-sdk-go/service/ecs" ecs "github.com/aws/aws-sdk-go/service/ecs"
gomock "github.com/golang/mock/gomock"
compose "github.com/docker/compose-cli/api/compose" compose "github.com/docker/compose-cli/api/compose"
secrets "github.com/docker/compose-cli/api/secrets" secrets "github.com/docker/compose-cli/api/secrets"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
) )
// MockAPI is a mock of API interface // MockAPI is a mock of API interface
@ -546,18 +544,18 @@ func (mr *MockAPIMockRecorder) ListStackServices(arg0, arg1 interface{}) *gomock
} }
// ListStacks mocks base method // ListStacks mocks base method
func (m *MockAPI) ListStacks(arg0 context.Context, arg1 string) ([]compose.Stack, error) { func (m *MockAPI) ListStacks(arg0 context.Context) ([]compose.Stack, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListStacks", arg0, arg1) ret := m.ctrl.Call(m, "ListStacks", arg0)
ret0, _ := ret[0].([]compose.Stack) ret0, _ := ret[0].([]compose.Stack)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// ListStacks indicates an expected call of ListStacks // ListStacks indicates an expected call of ListStacks
func (mr *MockAPIMockRecorder) ListStacks(arg0, arg1 interface{}) *gomock.Call { func (mr *MockAPIMockRecorder) ListStacks(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListStacks", reflect.TypeOf((*MockAPI)(nil).ListStacks), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListStacks", reflect.TypeOf((*MockAPI)(nil).ListStacks), arg0)
} }
// ListTasks mocks base method // ListTasks mocks base method

View File

@ -122,7 +122,7 @@ func TestCompose(t *testing.T) {
}) })
t.Run("compose ls", func(t *testing.T) { t.Run("compose ls", func(t *testing.T) {
res := c.RunDockerCmd("compose", "ls", "--project-name", stack) res := c.RunDockerCmd("compose", "ls", "--filter", "name="+stack)
lines := strings.Split(strings.TrimSpace(res.Stdout()), "\n") lines := strings.Split(strings.TrimSpace(res.Stdout()), "\n")
assert.Equal(t, 2, len(lines)) assert.Equal(t, 2, len(lines))

View File

@ -23,8 +23,8 @@ import (
"github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/compose"
) )
func (b *ecsAPIService) List(ctx context.Context, project string) ([]compose.Stack, error) { func (b *ecsAPIService) List(ctx context.Context) ([]compose.Stack, error) {
stacks, err := b.aws.ListStacks(ctx, project) stacks, err := b.aws.ListStacks(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -161,8 +161,8 @@ func (e ecsLocalSimulation) Logs(ctx context.Context, projectName string, consum
func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) { func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) {
return e.compose.Ps(ctx, projectName) return e.compose.Ps(ctx, projectName)
} }
func (e ecsLocalSimulation) List(ctx context.Context, projectName string) ([]compose.Stack, error) { func (e ecsLocalSimulation) List(ctx context.Context) ([]compose.Stack, error) {
return e.compose.List(ctx, projectName) return e.compose.List(ctx)
} }
func (e ecsLocalSimulation) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { func (e ecsLocalSimulation) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
return errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose run") return errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose run")

View File

@ -444,11 +444,8 @@ func (s sdk) GetStackID(ctx context.Context, name string) (string, error) {
return *stacks.Stacks[0].StackId, nil return *stacks.Stacks[0].StackId, nil
} }
func (s sdk) ListStacks(ctx context.Context, name string) ([]compose.Stack, error) { func (s sdk) ListStacks(ctx context.Context) ([]compose.Stack, error) {
params := cloudformation.DescribeStacksInput{} params := cloudformation.DescribeStacksInput{}
if name != "" {
params.StackName = &name
}
var token *string var token *string
var stacks []compose.Stack var stacks []compose.Stack
for { for {

View File

@ -27,7 +27,7 @@ import (
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
) )
func (s *composeService) List(ctx context.Context, projectName string) ([]compose.Stack, error) { func (s *composeService) List(ctx context.Context) ([]compose.Stack, error) {
list, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ list, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(hasProjectLabelFilter()), Filters: filters.NewArgs(hasProjectLabelFilter()),
}) })