mirror of https://github.com/docker/compose.git
if command is ran with a compose file, apply the compose model, not just project name
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
765c071c89
commit
f6e96dd783
|
@ -136,6 +136,20 @@ func (o *projectOptions) addProjectFlags(f *pflag.FlagSet) {
|
|||
_ = f.MarkHidden("workdir")
|
||||
}
|
||||
|
||||
func (o *projectOptions) projectOrName() (*types.Project, string, error) {
|
||||
name := o.ProjectName
|
||||
var project *types.Project
|
||||
if o.ProjectName == "" {
|
||||
p, err := o.toProject(nil)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
project = p
|
||||
name = p.Name
|
||||
}
|
||||
return project, name, nil
|
||||
}
|
||||
|
||||
func (o *projectOptions) toProjectName() (string, error) {
|
||||
if o.ProjectName != "" {
|
||||
return o.ProjectName, nil
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -79,15 +78,9 @@ func downCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||
}
|
||||
|
||||
func runDown(ctx context.Context, backend api.Service, opts downOptions) error {
|
||||
name := opts.ProjectName
|
||||
var project *types.Project
|
||||
if opts.ProjectName == "" {
|
||||
p, err := opts.toProject(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
project = p
|
||||
name = p.Name
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var timeout *time.Duration
|
||||
|
|
|
@ -51,12 +51,12 @@ func eventsCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||
}
|
||||
|
||||
func runEvents(ctx context.Context, backend api.Service, opts eventsOpts, services []string) error {
|
||||
project, err := opts.toProjectName()
|
||||
name, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Events(ctx, project, api.EventsOptions{
|
||||
return backend.Events(ctx, name, api.EventsOptions{
|
||||
Services: services,
|
||||
Consumer: func(event api.Event) error {
|
||||
if opts.json {
|
||||
|
|
|
@ -49,12 +49,12 @@ func killCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||
}
|
||||
|
||||
func runKill(ctx context.Context, backend api.Service, opts killOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName()
|
||||
name, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Kill(ctx, projectName, api.KillOptions{
|
||||
return backend.Kill(ctx, name, api.KillOptions{
|
||||
Services: services,
|
||||
Signal: opts.signal,
|
||||
})
|
||||
|
|
|
@ -44,13 +44,14 @@ func pauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||
}
|
||||
|
||||
func runPause(ctx context.Context, backend api.Service, opts pauseOptions, services []string) error {
|
||||
project, err := opts.toProjectName()
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Pause(ctx, project, api.PauseOptions{
|
||||
return backend.Pause(ctx, name, api.PauseOptions{
|
||||
Services: services,
|
||||
Project: project,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -74,12 +75,13 @@ func unpauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||
}
|
||||
|
||||
func runUnPause(ctx context.Context, backend api.Service, opts unpauseOptions, services []string) error {
|
||||
project, err := opts.toProjectName()
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.UnPause(ctx, project, api.PauseOptions{
|
||||
return backend.UnPause(ctx, name, api.PauseOptions{
|
||||
Services: services,
|
||||
Project: project,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -59,23 +59,25 @@ Any data which is not in a volume will be lost.`,
|
|||
}
|
||||
|
||||
func runRemove(ctx context.Context, backend api.Service, opts removeOptions, services []string) error {
|
||||
project, err := opts.toProjectName()
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.stop {
|
||||
err := backend.Stop(ctx, project, api.StopOptions{
|
||||
err := backend.Stop(ctx, name, api.StopOptions{
|
||||
Services: services,
|
||||
Project: project,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return backend.Remove(ctx, project, api.RemoveOptions{
|
||||
return backend.Remove(ctx, name, api.RemoveOptions{
|
||||
Services: services,
|
||||
Force: opts.force,
|
||||
Volumes: opts.volumes,
|
||||
Project: project,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -49,14 +49,15 @@ func restartCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||
}
|
||||
|
||||
func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName()
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeout := time.Duration(opts.timeout) * time.Second
|
||||
return backend.Restart(ctx, projectName, api.RestartOptions{
|
||||
return backend.Restart(ctx, name, api.RestartOptions{
|
||||
Timeout: &timeout,
|
||||
Services: services,
|
||||
Project: project,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -43,12 +43,13 @@ func startCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||
}
|
||||
|
||||
func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName()
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Start(ctx, projectName, api.StartOptions{
|
||||
return backend.Start(ctx, name, api.StartOptions{
|
||||
AttachTo: services,
|
||||
Project: project,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ func stopCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||
}
|
||||
|
||||
func runStop(ctx context.Context, backend api.Service, opts stopOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName()
|
||||
project, name, err := opts.projectOrName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -63,8 +63,9 @@ func runStop(ctx context.Context, backend api.Service, opts stopOptions, service
|
|||
timeoutValue := time.Duration(opts.timeout) * time.Second
|
||||
timeout = &timeoutValue
|
||||
}
|
||||
return backend.Stop(ctx, projectName, api.StopOptions{
|
||||
return backend.Stop(ctx, name, api.StopOptions{
|
||||
Timeout: timeout,
|
||||
Services: services,
|
||||
Project: project,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ type CreateOptions struct {
|
|||
|
||||
// StartOptions group options of the Start API
|
||||
type StartOptions struct {
|
||||
// Project is the compose project used to define this app. Might be nil if user ran `start` just with project name
|
||||
// Project is the compose project used to define this app. Might be nil if user ran command just with project name
|
||||
Project *types.Project
|
||||
// Attach to container and forward logs if not nil
|
||||
Attach LogConsumer
|
||||
|
@ -133,6 +133,8 @@ type StartOptions struct {
|
|||
|
||||
// RestartOptions group options of the Restart API
|
||||
type RestartOptions struct {
|
||||
// Project is the compose project used to define this app. Might be nil if user ran command just with project name
|
||||
Project *types.Project
|
||||
// Timeout override container restart timeout
|
||||
Timeout *time.Duration
|
||||
// Services passed in the command line to be restarted
|
||||
|
@ -141,6 +143,8 @@ type RestartOptions struct {
|
|||
|
||||
// StopOptions group options of the Stop API
|
||||
type StopOptions struct {
|
||||
// Project is the compose project used to define this app. Might be nil if user ran command just with project name
|
||||
Project *types.Project
|
||||
// Timeout override container stop timeout
|
||||
Timeout *time.Duration
|
||||
// Services passed in the command line to be stopped
|
||||
|
@ -201,6 +205,8 @@ type KillOptions struct {
|
|||
|
||||
// RemoveOptions group options of the Remove API
|
||||
type RemoveOptions struct {
|
||||
// Project is the compose project used to define this app. Might be nil if user ran command just with project name
|
||||
Project *types.Project
|
||||
// DryRun just list removable resources
|
||||
DryRun bool
|
||||
// Volumes remove anonymous volumes
|
||||
|
@ -213,6 +219,8 @@ type RemoveOptions struct {
|
|||
|
||||
// RunOptions group options of the Run API
|
||||
type RunOptions struct {
|
||||
// Project is the compose project used to define this app. Might be nil if user ran command just with project name
|
||||
Project *types.Project
|
||||
Name string
|
||||
Service string
|
||||
Command []string
|
||||
|
@ -377,6 +385,8 @@ type LogOptions struct {
|
|||
type PauseOptions struct {
|
||||
// Services passed in the command line to be started
|
||||
Services []string
|
||||
// Project is the compose project used to define this app. Might be nil if user ran command just with project name
|
||||
Project *types.Project
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
@ -172,26 +172,6 @@ SERVICES:
|
|||
return project, nil
|
||||
}
|
||||
|
||||
// actualState list resources labelled by projectName to rebuild compose project model
|
||||
func (s *composeService) actualState(ctx context.Context, projectName string, services []string) (Containers, *types.Project, error) {
|
||||
var containers Containers
|
||||
// don't filter containers by options.Services so projectFromName can rebuild project with all existing resources
|
||||
containers, err := s.getContainers(ctx, projectName, oneOffInclude, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
project, err := s.projectFromName(containers, projectName, services...)
|
||||
if err != nil && !api.IsNotFoundError(err) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(services) > 0 {
|
||||
containers = containers.filter(isService(services...))
|
||||
}
|
||||
return containers, project, nil
|
||||
}
|
||||
|
||||
func (s *composeService) actualVolumes(ctx context.Context, projectName string) (types.Volumes, error) {
|
||||
volumes, err := s.apiClient().VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
|
||||
if err != nil {
|
||||
|
|
|
@ -45,8 +45,11 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
|
|||
w := progress.ContextWriter(ctx)
|
||||
resourceToRemove := false
|
||||
|
||||
var containers Containers
|
||||
containers, err := s.getContainers(ctx, projectName, oneOffInclude, true)
|
||||
include := oneOffExclude
|
||||
if options.RemoveOrphans {
|
||||
include = oneOffInclude
|
||||
}
|
||||
containers, err := s.getContainers(ctx, projectName, include, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ func TestDown(t *testing.T) {
|
|||
tested.dockerCli = cli
|
||||
cli.EXPECT().Client().Return(api).AnyTimes()
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]moby.Container{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "456", false),
|
||||
|
@ -88,7 +88,7 @@ func TestDownRemoveOrphans(t *testing.T) {
|
|||
tested.dockerCli = cli
|
||||
cli.EXPECT().Client().Return(api).AnyTimes()
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(true)).Return(
|
||||
[]moby.Container{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "789", false),
|
||||
|
@ -125,7 +125,7 @@ func TestDownRemoveVolumes(t *testing.T) {
|
|||
tested.dockerCli = cli
|
||||
cli.EXPECT().Client().Return(api).AnyTimes()
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]moby.Container{testContainer("service1", "123", false)}, nil)
|
||||
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
|
||||
Return(volume.VolumeListOKBody{
|
||||
|
|
|
@ -18,6 +18,7 @@ package compose
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -110,9 +111,15 @@ func anyCancellableContext() gomock.Matcher {
|
|||
return gomock.AssignableToTypeOf(ctxWithCancel)
|
||||
}
|
||||
|
||||
func projectFilterListOpt() moby.ContainerListOptions {
|
||||
func projectFilterListOpt(withOneOff bool) moby.ContainerListOptions {
|
||||
filter := filters.NewArgs(
|
||||
projectFilter(strings.ToLower(testProject)),
|
||||
)
|
||||
if !withOneOff {
|
||||
filter.Add("label", fmt.Sprintf("%s=False", compose.OneoffLabel))
|
||||
}
|
||||
return moby.ContainerListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
Filters: filter,
|
||||
All: true,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,12 +33,16 @@ func (s *composeService) Pause(ctx context.Context, projectName string, options
|
|||
})
|
||||
}
|
||||
|
||||
func (s *composeService) pause(ctx context.Context, project string, options api.PauseOptions) error {
|
||||
containers, err := s.getContainers(ctx, project, oneOffExclude, false, options.Services...)
|
||||
func (s *composeService) pause(ctx context.Context, projectName string, options api.PauseOptions) error {
|
||||
containers, err := s.getContainers(ctx, projectName, oneOffExclude, false, options.Services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if options.Project != nil {
|
||||
containers = containers.filter(isService(options.Project.ServiceNames()...))
|
||||
}
|
||||
|
||||
w := progress.ContextWriter(ctx)
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
containers.forEach(func(container moby.Container) {
|
||||
|
@ -67,6 +71,10 @@ func (s *composeService) unPause(ctx context.Context, projectName string, option
|
|||
return err
|
||||
}
|
||||
|
||||
if options.Project != nil {
|
||||
containers = containers.filter(isService(options.Project.ServiceNames()...))
|
||||
}
|
||||
|
||||
w := progress.ContextWriter(ctx)
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
containers.forEach(func(container moby.Container) {
|
||||
|
|
|
@ -31,7 +31,7 @@ import (
|
|||
|
||||
func (s *composeService) Remove(ctx context.Context, projectName string, options api.RemoveOptions) error {
|
||||
projectName = strings.ToLower(projectName)
|
||||
containers, _, err := s.actualState(ctx, projectName, options.Services)
|
||||
containers, err := s.getContainers(ctx, projectName, oneOffExclude, true, options.Services...)
|
||||
if err != nil {
|
||||
if api.IsNotFoundError(err) {
|
||||
fmt.Fprintln(s.stderr(), "No stopped containers")
|
||||
|
@ -40,6 +40,10 @@ func (s *composeService) Remove(ctx context.Context, projectName string, options
|
|||
return err
|
||||
}
|
||||
|
||||
if options.Project != nil {
|
||||
containers = containers.filter(isService(options.Project.ServiceNames()...))
|
||||
}
|
||||
|
||||
stoppedContainers := containers.filter(func(c moby.Container) bool {
|
||||
return c.State != ContainerRunning
|
||||
})
|
||||
|
|
|
@ -34,15 +34,17 @@ func (s *composeService) Restart(ctx context.Context, projectName string, option
|
|||
}
|
||||
|
||||
func (s *composeService) restart(ctx context.Context, projectName string, options api.RestartOptions) error {
|
||||
|
||||
observedState, err := s.getContainers(ctx, projectName, oneOffExclude, true)
|
||||
containers, err := s.getContainers(ctx, projectName, oneOffExclude, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project, err := s.projectFromName(observedState, projectName, options.Services...)
|
||||
if err != nil {
|
||||
return err
|
||||
project := options.Project
|
||||
if project == nil {
|
||||
project, err = s.getProjectWithResources(ctx, containers, projectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(options.Services) == 0 {
|
||||
|
@ -50,12 +52,12 @@ func (s *composeService) restart(ctx context.Context, projectName string, option
|
|||
}
|
||||
|
||||
w := progress.ContextWriter(ctx)
|
||||
err = InDependencyOrder(ctx, project, func(c context.Context, service string) error {
|
||||
return InDependencyOrder(ctx, project, func(c context.Context, service string) error {
|
||||
if !utils.StringContains(options.Services, service) {
|
||||
return nil
|
||||
}
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for _, container := range observedState.filter(isService(service)) {
|
||||
for _, container := range containers.filter(isService(service)) {
|
||||
container := container
|
||||
eg.Go(func() error {
|
||||
eventName := getContainerProgressName(container)
|
||||
|
@ -69,8 +71,4 @@ func (s *composeService) restart(ctx context.Context, projectName string, option
|
|||
}
|
||||
return eg.Wait()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
)
|
||||
|
||||
func (s *composeService) Stop(ctx context.Context, projectName string, options api.StopOptions) error {
|
||||
|
@ -31,15 +32,28 @@ func (s *composeService) Stop(ctx context.Context, projectName string, options a
|
|||
}
|
||||
|
||||
func (s *composeService) stop(ctx context.Context, projectName string, options api.StopOptions) error {
|
||||
w := progress.ContextWriter(ctx)
|
||||
|
||||
containers, project, err := s.actualState(ctx, projectName, options.Services)
|
||||
containers, err := s.getContainers(ctx, projectName, oneOffExclude, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project := options.Project
|
||||
if project == nil {
|
||||
project, err = s.getProjectWithResources(ctx, containers, projectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(options.Services) == 0 {
|
||||
options.Services = project.ServiceNames()
|
||||
}
|
||||
|
||||
w := progress.ContextWriter(ctx)
|
||||
return InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
|
||||
containersToStop := containers.filter(isService(service)).filter(isNotOneOff)
|
||||
return s.stopContainers(ctx, w, containersToStop, options.Timeout)
|
||||
if !utils.StringContains(options.Services, service) {
|
||||
return nil
|
||||
}
|
||||
return s.stopContainers(ctx, w, containers.filter(isService(service)).filter(isNotOneOff), options.Timeout)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import (
|
|||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/golang/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
@ -40,12 +42,16 @@ func TestStopTimeout(t *testing.T) {
|
|||
cli.EXPECT().Client().Return(api).AnyTimes()
|
||||
|
||||
ctx := context.Background()
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]moby.Container{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service1", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
}, nil)
|
||||
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
|
||||
Return(volume.VolumeListOKBody{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return([]moby.NetworkResource{}, nil)
|
||||
|
||||
timeout := time.Duration(2) * time.Second
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", &timeout).Return(nil)
|
||||
|
|
|
@ -79,7 +79,7 @@ func TestLocalComposeRun(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("down", func(t *testing.T) {
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "down")
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "down", "--remove-orphans")
|
||||
res := c.RunDockerCmd(t, "ps", "--all")
|
||||
assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
|
||||
})
|
||||
|
@ -121,6 +121,7 @@ func TestLocalComposeRun(t *testing.T) {
|
|||
c.Env = append(c.Env, "COMPOSE_REMOVE_ORPHANS=True")
|
||||
})
|
||||
res := c.RunDockerCmd(t, "ps", "--all")
|
||||
|
||||
assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue