diff --git a/pkg/compose/containers.go b/pkg/compose/containers.go index ca2d0ca1..31ea5c8a 100644 --- a/pkg/compose/containers.go +++ b/pkg/compose/containers.go @@ -18,14 +18,13 @@ package compose import ( "context" + "fmt" "sort" - "strconv" - - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/utils" + moby "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" ) // Containers is a set of moby Container @@ -41,17 +40,7 @@ const ( func (s *composeService) getContainers(ctx context.Context, project string, oneOff oneOff, stopped bool, selectedServices ...string) (Containers, error) { var containers Containers - f := []filters.KeyValuePair{projectFilter(project)} - if len(selectedServices) == 1 { - f = append(f, serviceFilter(selectedServices[0])) - } - switch oneOff { - case oneOffOnly: - f = append(f, oneOffFilter(true)) - case oneOffExclude: - f = append(f, oneOffFilter(false)) - case oneOffInclude: - } + f := getDefaultFilters(project, oneOff, selectedServices...) containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{ Filters: filters.NewArgs(f...), All: stopped, @@ -65,6 +54,40 @@ func (s *composeService) getContainers(ctx context.Context, project string, oneO return containers, nil } +func getDefaultFilters(projectName string, oneOff oneOff, selectedServices ...string) []filters.KeyValuePair { + f := []filters.KeyValuePair{projectFilter(projectName)} + if len(selectedServices) == 1 { + f = append(f, serviceFilter(selectedServices[0])) + } + switch oneOff { + case oneOffOnly: + f = append(f, oneOffFilter(true)) + case oneOffExclude: + f = append(f, oneOffFilter(false)) + case oneOffInclude: + } + return f +} + +func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, stopped bool, serviceName string, containerIndex int) (moby.Container, error) { + defaultFilters := getDefaultFilters(projectName, oneOff, serviceName) + defaultFilters = append(defaultFilters, containerNumberFilter(containerIndex)) + containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{ + Filters: filters.NewArgs( + defaultFilters..., + ), + All: stopped, + }) + if err != nil { + return moby.Container{}, err + } + if len(containers) < 1 { + return moby.Container{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex) + } + container := containers[0] + return container, nil +} + // containerPredicate define a predicate we want container to satisfy for filtering operations type containerPredicate func(c moby.Container) bool @@ -87,14 +110,6 @@ func isNotOneOff(c moby.Container) bool { return !ok || v == "False" } -func indexed(index int) containerPredicate { - return func(c moby.Container) bool { - number := c.Labels[api.ContainerNumberLabel] - idx, err := strconv.Atoi(number) - return err == nil && index == idx - } -} - // filter return Containers with elements to match predicate func (containers Containers) filter(predicate containerPredicate) Containers { var filtered Containers diff --git a/pkg/compose/cp.go b/pkg/compose/cp.go index fe8f82fe..b9aefbf7 100644 --- a/pkg/compose/cp.go +++ b/pkg/compose/cp.go @@ -66,18 +66,23 @@ func (s *composeService) Copy(ctx context.Context, projectName string, options a direction |= toService serviceName = destService } - - containers, err := s.getContainers(ctx, projectName, oneOffExclude, true, serviceName) - if err != nil { - return err - } - - if len(containers) < 1 { - return fmt.Errorf("no container found for service %q", serviceName) - } - + var containers Containers + var err error if direction == fromService || (direction == toService && options.Index > 0) { - containers = containers.filter(indexed(options.Index)) + container, err := s.getSpecifiedContainer(ctx, projectName, oneOffExclude, true, serviceName, options.Index) + if err != nil { + return err + } + containers = append(containers, container) + } else { + containers, err = s.getContainers(ctx, projectName, oneOffExclude, true, serviceName) + if err != nil { + return err + } + + if len(containers) < 1 { + return fmt.Errorf("no container found for service %q", serviceName) + } } g := errgroup.Group{} diff --git a/pkg/compose/exec.go b/pkg/compose/exec.go index aa1e5c58..6477ca06 100644 --- a/pkg/compose/exec.go +++ b/pkg/compose/exec.go @@ -18,14 +18,12 @@ package compose import ( "context" - "fmt" "strings" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command/container" "github.com/docker/compose/v2/pkg/api" moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" ) func (s *composeService) Exec(ctx context.Context, projectName string, options api.RunOptions) (int, error) { @@ -59,19 +57,5 @@ func (s *composeService) Exec(ctx context.Context, projectName string, options a } func (s *composeService) getExecTarget(ctx context.Context, projectName string, opts api.RunOptions) (moby.Container, error) { - containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{ - Filters: filters.NewArgs( - projectFilter(projectName), - serviceFilter(opts.Service), - containerNumberFilter(opts.Index), - ), - }) - if err != nil { - return moby.Container{}, err - } - if len(containers) < 1 { - return moby.Container{}, fmt.Errorf("service %q is not running container #%d", opts.Service, opts.Index) - } - container := containers[0] - return container, nil + return s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, opts.Service, opts.Index) }