compose cp doesn't need a full project and can copy from stopped container

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2021-12-06 14:54:42 +01:00
parent e2f33af831
commit c5b7624d10
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
5 changed files with 21 additions and 20 deletions

View File

@ -73,12 +73,12 @@ func copyCommand(p *projectOptions, backend api.Service) *cobra.Command {
} }
func runCopy(ctx context.Context, backend api.Service, opts copyOptions) error { func runCopy(ctx context.Context, backend api.Service, opts copyOptions) error {
projects, err := opts.toProject(nil) name, err := opts.toProjectName()
if err != nil { if err != nil {
return err return err
} }
return backend.Copy(ctx, projects, api.CopyOptions{ return backend.Copy(ctx, name, api.CopyOptions{
Source: opts.source, Source: opts.source,
Destination: opts.destination, Destination: opts.destination,
All: opts.all, All: opts.all,

View File

@ -63,7 +63,7 @@ type Service interface {
// Exec executes a command in a running service container // Exec executes a command in a running service container
Exec(ctx context.Context, project string, opts RunOptions) (int, error) Exec(ctx context.Context, project string, opts RunOptions) (int, error)
// Copy copies a file/folder between a service container and the local filesystem // Copy copies a file/folder between a service container and the local filesystem
Copy(ctx context.Context, project *types.Project, opts CopyOptions) error Copy(ctx context.Context, project string, options CopyOptions) error
// Pause executes the equivalent to a `compose pause` // Pause executes the equivalent to a `compose pause`
Pause(ctx context.Context, project string, options PauseOptions) error Pause(ctx context.Context, project string, options PauseOptions) error
// UnPause executes the equivalent to a `compose unpause` // UnPause executes the equivalent to a `compose unpause`

View File

@ -41,7 +41,7 @@ type ServiceProxy struct {
RunOneOffContainerFn func(ctx context.Context, project *types.Project, opts RunOptions) (int, error) RunOneOffContainerFn func(ctx context.Context, project *types.Project, opts RunOptions) (int, error)
RemoveFn func(ctx context.Context, project *types.Project, options RemoveOptions) error RemoveFn func(ctx context.Context, project *types.Project, options RemoveOptions) error
ExecFn func(ctx context.Context, project string, opts RunOptions) (int, error) ExecFn func(ctx context.Context, project string, opts RunOptions) (int, error)
CopyFn func(ctx context.Context, project *types.Project, opts CopyOptions) error CopyFn func(ctx context.Context, project string, options CopyOptions) error
PauseFn func(ctx context.Context, project string, options PauseOptions) error PauseFn func(ctx context.Context, project string, options PauseOptions) error
UnPauseFn func(ctx context.Context, project string, options PauseOptions) error UnPauseFn func(ctx context.Context, project string, options PauseOptions) error
TopFn func(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error) TopFn func(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
@ -269,13 +269,10 @@ func (s *ServiceProxy) Exec(ctx context.Context, project string, options RunOpti
} }
// Copy implements Service interface // Copy implements Service interface
func (s *ServiceProxy) Copy(ctx context.Context, project *types.Project, options CopyOptions) error { func (s *ServiceProxy) Copy(ctx context.Context, project string, options CopyOptions) error {
if s.CopyFn == nil { if s.CopyFn == nil {
return ErrNotImplemented return ErrNotImplemented
} }
for _, i := range s.interceptors {
i(ctx, project)
}
return s.CopyFn(ctx, project, options) return s.CopyFn(ctx, project, options)
} }

View File

@ -19,6 +19,7 @@ package compose
import ( import (
"context" "context"
"sort" "sort"
"strconv"
moby "github.com/docker/docker/api/types" moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
@ -86,6 +87,14 @@ func isNotOneOff(c moby.Container) bool {
return !ok || v == "False" 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 // filter return Containers with elements to match predicate
func (containers Containers) filter(predicate containerPredicate) Containers { func (containers Containers) filter(predicate containerPredicate) Containers {
var filtered Containers var filtered Containers

View File

@ -26,11 +26,9 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/api"
moby "github.com/docker/docker/api/types" moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -44,7 +42,7 @@ const (
acrossServices = fromService | toService acrossServices = fromService | toService
) )
func (s *composeService) Copy(ctx context.Context, project *types.Project, opts api.CopyOptions) error { func (s *composeService) Copy(ctx context.Context, project string, opts api.CopyOptions) error {
srcService, srcPath := splitCpArg(opts.Source) srcService, srcPath := splitCpArg(opts.Source)
destService, dstPath := splitCpArg(opts.Destination) destService, dstPath := splitCpArg(opts.Destination)
@ -64,20 +62,17 @@ func (s *composeService) Copy(ctx context.Context, project *types.Project, opts
serviceName = destService serviceName = destService
} }
f := filters.NewArgs( containers, err := s.getContainers(ctx, project, oneOffExclude, true, serviceName)
projectFilter(project.Name),
serviceFilter(serviceName),
)
if !opts.All {
f.Add("label", fmt.Sprintf("%s=%d", api.ContainerNumberLabel, opts.Index))
}
containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{Filters: f})
if err != nil { if err != nil {
return err return err
} }
if len(containers) < 1 { if len(containers) < 1 {
return fmt.Errorf("service %s not running", serviceName) return fmt.Errorf("no container found for service %q", serviceName)
}
if !opts.All {
containers = containers.filter(indexed(opts.Index))
} }
g := errgroup.Group{} g := errgroup.Group{}