From 65ed8cf4c29925c387d51906ebd260627d3bd20b Mon Sep 17 00:00:00 2001 From: Mehrad Dadar Date: Sat, 5 Feb 2022 10:27:52 +0330 Subject: [PATCH] Implemented #9147 Signed-off-by: Mehrad Dadar --- cmd/compose/remove.go | 2 +- cmd/compose/restart.go | 4 ++-- cmd/compose/run.go | 2 +- cmd/compose/start.go | 6 ++++-- cmd/compose/stop.go | 4 ++-- pkg/api/api.go | 6 +++--- pkg/api/proxy.go | 27 +++++++++------------------ pkg/compose/compose.go | 31 +++++++++++++++++++++++++++++++ pkg/compose/restart.go | 12 +++++++----- pkg/compose/start.go | 16 ++++++++++------ pkg/compose/stop.go | 16 +++++++++------- pkg/compose/stop_test.go | 9 +-------- pkg/compose/up.go | 6 +++--- 13 files changed, 83 insertions(+), 58 deletions(-) diff --git a/cmd/compose/remove.go b/cmd/compose/remove.go index f84621387..b4b61d580 100644 --- a/cmd/compose/remove.go +++ b/cmd/compose/remove.go @@ -65,7 +65,7 @@ func runRemove(ctx context.Context, backend api.Service, opts removeOptions, ser } if opts.stop { - err := backend.Stop(ctx, project, api.StopOptions{ + err := backend.Stop(ctx, project.Name, api.StopOptions{ Services: services, }) if err != nil { diff --git a/cmd/compose/restart.go b/cmd/compose/restart.go index 600d2b6fa..f691c31fb 100644 --- a/cmd/compose/restart.go +++ b/cmd/compose/restart.go @@ -49,13 +49,13 @@ func restartCommand(p *projectOptions, backend api.Service) *cobra.Command { } func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error { - project, err := opts.toProject(services) + projectName, err := opts.toProjectName() if err != nil { return err } timeout := time.Duration(opts.timeout) * time.Second - return backend.Restart(ctx, project, api.RestartOptions{ + return backend.Restart(ctx, projectName, api.RestartOptions{ Timeout: &timeout, Services: services, }) diff --git a/cmd/compose/run.go b/cmd/compose/run.go index fc5f76421..575026204 100644 --- a/cmd/compose/run.go +++ b/cmd/compose/run.go @@ -240,5 +240,5 @@ func startDependencies(ctx context.Context, backend api.Service, project types.P if err := backend.Create(ctx, &project, api.CreateOptions{}); err != nil { return err } - return backend.Start(ctx, &project, api.StartOptions{}) + return backend.Start(ctx, project.Name, api.StartOptions{}) } diff --git a/cmd/compose/start.go b/cmd/compose/start.go index 8ad99c566..e9b67ae44 100644 --- a/cmd/compose/start.go +++ b/cmd/compose/start.go @@ -43,10 +43,12 @@ func startCommand(p *projectOptions, backend api.Service) *cobra.Command { } func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error { - project, err := opts.toProject(services) + projectName, err := opts.toProjectName() if err != nil { return err } - return backend.Start(ctx, project, api.StartOptions{}) + return backend.Start(ctx, projectName, api.StartOptions{ + AttachTo: services, + }) } diff --git a/cmd/compose/stop.go b/cmd/compose/stop.go index d00d94f9d..39861cd68 100644 --- a/cmd/compose/stop.go +++ b/cmd/compose/stop.go @@ -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 { - project, err := opts.toProject(services) + projectName, err := opts.toProjectName() if err != nil { return err } @@ -63,7 +63,7 @@ func runStop(ctx context.Context, backend api.Service, opts stopOptions, service timeoutValue := time.Duration(opts.timeout) * time.Second timeout = &timeoutValue } - return backend.Stop(ctx, project, api.StopOptions{ + return backend.Stop(ctx, projectName, api.StopOptions{ Timeout: timeout, Services: services, }) diff --git a/pkg/api/api.go b/pkg/api/api.go index 5d963569c..9f9f0c038 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -37,11 +37,11 @@ type Service interface { // Create executes the equivalent to a `compose create` Create(ctx context.Context, project *types.Project, opts CreateOptions) error // Start executes the equivalent to a `compose start` - Start(ctx context.Context, project *types.Project, options StartOptions) error + Start(ctx context.Context, projectName string, options StartOptions) error // Restart restarts containers - Restart(ctx context.Context, project *types.Project, options RestartOptions) error + Restart(ctx context.Context, projectName string, options RestartOptions) error // Stop executes the equivalent to a `compose stop` - Stop(ctx context.Context, project *types.Project, options StopOptions) error + Stop(ctx context.Context, projectName string, options StopOptions) error // Up executes the equivalent to a `compose up` Up(ctx context.Context, project *types.Project, options UpOptions) error // Down executes the equivalent to a `compose down` diff --git a/pkg/api/proxy.go b/pkg/api/proxy.go index 6c3fe22f3..9a06b9cb4 100644 --- a/pkg/api/proxy.go +++ b/pkg/api/proxy.go @@ -28,9 +28,9 @@ type ServiceProxy struct { PushFn func(ctx context.Context, project *types.Project, options PushOptions) error PullFn func(ctx context.Context, project *types.Project, opts PullOptions) error CreateFn func(ctx context.Context, project *types.Project, opts CreateOptions) error - StartFn func(ctx context.Context, project *types.Project, options StartOptions) error - RestartFn func(ctx context.Context, project *types.Project, options RestartOptions) error - StopFn func(ctx context.Context, project *types.Project, options StopOptions) error + StartFn func(ctx context.Context, projectName string, options StartOptions) error + RestartFn func(ctx context.Context, projectName string, options RestartOptions) error + StopFn func(ctx context.Context, projectName string, options StopOptions) error UpFn func(ctx context.Context, project *types.Project, options UpOptions) error DownFn func(ctx context.Context, projectName string, options DownOptions) error LogsFn func(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error @@ -141,36 +141,27 @@ func (s *ServiceProxy) Create(ctx context.Context, project *types.Project, optio } // Start implements Service interface -func (s *ServiceProxy) Start(ctx context.Context, project *types.Project, options StartOptions) error { +func (s *ServiceProxy) Start(ctx context.Context, projectName string, options StartOptions) error { if s.StartFn == nil { return ErrNotImplemented } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.StartFn(ctx, project, options) + return s.StartFn(ctx, projectName, options) } // Restart implements Service interface -func (s *ServiceProxy) Restart(ctx context.Context, project *types.Project, options RestartOptions) error { +func (s *ServiceProxy) Restart(ctx context.Context, projectName string, options RestartOptions) error { if s.RestartFn == nil { return ErrNotImplemented } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.RestartFn(ctx, project, options) + return s.RestartFn(ctx, projectName, options) } // Stop implements Service interface -func (s *ServiceProxy) Stop(ctx context.Context, project *types.Project, options StopOptions) error { +func (s *ServiceProxy) Stop(ctx context.Context, projectName string, options StopOptions) error { if s.StopFn == nil { return ErrNotImplemented } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.StopFn(ctx, project, options) + return s.StopFn(ctx, projectName, options) } // Up implements Service interface diff --git a/pkg/compose/compose.go b/pkg/compose/compose.go index 7c31218cd..0a7781ae8 100644 --- a/pkg/compose/compose.go +++ b/pkg/compose/compose.go @@ -92,3 +92,34 @@ func escapeDollarSign(marshal []byte) []byte { escDollar := []byte{'$', '$'} return bytes.ReplaceAll(marshal, dollar, escDollar) } + +// projectFromName builds a types.Project based on actual resources with compose labels set +func (s *composeService) projectFromName(containers Containers, projectName string) *types.Project { + project := &types.Project{ + Name: projectName, + } + if len(containers) == 0 { + return project + } + set := map[string]moby.Container{} + for _, c := range containers { + set[c.Labels[api.ServiceLabel]] = c + } + for s, c := range set { + service := types.ServiceConfig{ + Name: s, + Image: c.Image, + Labels: c.Labels, + } + dependencies := c.Labels[api.DependenciesLabel] + if len(dependencies) > 0 { + service.DependsOn = types.DependsOnConfig{} + for _, d := range strings.Split(dependencies, ",") { + service.DependsOn[d] = types.ServiceDependency{} + } + } + project.Services = append(project.Services, service) + } + + return project +} diff --git a/pkg/compose/restart.go b/pkg/compose/restart.go index 44592c62f..4de3022e6 100644 --- a/pkg/compose/restart.go +++ b/pkg/compose/restart.go @@ -19,7 +19,6 @@ package compose import ( "context" - "github.com/compose-spec/compose-go/types" "github.com/docker/compose/v2/pkg/api" "golang.org/x/sync/errgroup" @@ -27,18 +26,21 @@ import ( "github.com/docker/compose/v2/pkg/utils" ) -func (s *composeService) Restart(ctx context.Context, project *types.Project, options api.RestartOptions) error { +func (s *composeService) Restart(ctx context.Context, projectName string, options api.RestartOptions) error { return progress.Run(ctx, func(ctx context.Context) error { - return s.restart(ctx, project, options) + return s.restart(ctx, projectName, options) }) } -func (s *composeService) restart(ctx context.Context, project *types.Project, options api.RestartOptions) error { - observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true) +func (s *composeService) restart(ctx context.Context, projectName string, options api.RestartOptions) error { + + observedState, err := s.getContainers(ctx, projectName, oneOffInclude, true) if err != nil { return err } + project := s.projectFromName(observedState, projectName) + if len(options.Services) == 0 { options.Services = project.ServiceNames() } diff --git a/pkg/compose/start.go b/pkg/compose/start.go index de31b3ea7..a42f4fcc6 100644 --- a/pkg/compose/start.go +++ b/pkg/compose/start.go @@ -28,17 +28,21 @@ import ( "github.com/docker/compose/v2/pkg/progress" ) -func (s *composeService) Start(ctx context.Context, project *types.Project, options api.StartOptions) error { +func (s *composeService) Start(ctx context.Context, projectName string, options api.StartOptions) error { return progress.Run(ctx, func(ctx context.Context) error { - return s.start(ctx, project, options, nil) + return s.start(ctx, projectName, options, nil) }) } -func (s *composeService) start(ctx context.Context, project *types.Project, options api.StartOptions, listener api.ContainerEventListener) error { - if len(options.AttachTo) == 0 { - options.AttachTo = project.ServiceNames() +func (s *composeService) start(ctx context.Context, projectName string, options api.StartOptions, listener api.ContainerEventListener) error { + var containers Containers + containers, err := s.getContainers(ctx, projectName, oneOffInclude, true, options.AttachTo...) + if err != nil { + return err } + project := s.projectFromName(containers, projectName) + eg, ctx := errgroup.WithContext(ctx) if listener != nil { attached, err := s.attach(ctx, project, listener, options.AttachTo) @@ -53,7 +57,7 @@ func (s *composeService) start(ctx context.Context, project *types.Project, opti }) } - err := InDependencyOrder(ctx, project, func(c context.Context, name string) error { + err = InDependencyOrder(ctx, project, func(c context.Context, name string) error { service, err := project.GetService(name) if err != nil { return err diff --git a/pkg/compose/stop.go b/pkg/compose/stop.go index 6655aa2b1..54c29dd08 100644 --- a/pkg/compose/stop.go +++ b/pkg/compose/stop.go @@ -21,29 +21,31 @@ import ( "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/progress" - - "github.com/compose-spec/compose-go/types" + //"github.com/compose-spec/compose-go/types" ) -func (s *composeService) Stop(ctx context.Context, project *types.Project, options api.StopOptions) error { +func (s *composeService) Stop(ctx context.Context, projectName string, options api.StopOptions) error { return progress.Run(ctx, func(ctx context.Context) error { - return s.stop(ctx, project, options) + return s.stop(ctx, projectName, options) }) } -func (s *composeService) stop(ctx context.Context, project *types.Project, options api.StopOptions) error { +func (s *composeService) stop(ctx context.Context, projectName string, options api.StopOptions) error { w := progress.ContextWriter(ctx) services := options.Services if len(services) == 0 { - services = project.ServiceNames() + services = []string{} } + var containers Containers - containers, err := s.getContainers(ctx, project.Name, oneOffInclude, true, services...) + containers, err := s.getContainers(ctx, projectName, oneOffInclude, true, services...) if err != nil { return err } + project := s.projectFromName(containers, projectName) + return InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error { return s.stopContainers(ctx, w, containers.filter(isService(service)), options.Timeout) }) diff --git a/pkg/compose/stop_test.go b/pkg/compose/stop_test.go index 4b8e345b6..29cecbd3f 100644 --- a/pkg/compose/stop_test.go +++ b/pkg/compose/stop_test.go @@ -25,7 +25,6 @@ import ( compose "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/mocks" - "github.com/compose-spec/compose-go/types" moby "github.com/docker/docker/api/types" "github.com/golang/mock/gomock" "gotest.tools/v3/assert" @@ -50,13 +49,7 @@ func TestStopTimeout(t *testing.T) { api.EXPECT().ContainerStop(gomock.Any(), "456", &timeout).Return(nil) api.EXPECT().ContainerStop(gomock.Any(), "789", &timeout).Return(nil) - err := tested.Stop(ctx, &types.Project{ - Name: strings.ToLower(testProject), - Services: []types.ServiceConfig{ - {Name: "service1"}, - {Name: "service2"}, - }, - }, compose.StopOptions{ + err := tested.Stop(ctx, strings.ToLower(testProject), compose.StopOptions{ Timeout: &timeout, }) assert.NilError(t, err) diff --git a/pkg/compose/up.go b/pkg/compose/up.go index 88d0df1f8..3ee07f1cc 100644 --- a/pkg/compose/up.go +++ b/pkg/compose/up.go @@ -38,7 +38,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options return err } if options.Start.Attach == nil { - return s.start(ctx, project, options.Start, nil) + return s.start(ctx, project.Name, options.Start, nil) } return nil }) @@ -65,7 +65,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options }) }() - return s.Stop(ctx, project, api.StopOptions{ + return s.Stop(ctx, project.Name, api.StopOptions{ Services: options.Create.Services, }) }) @@ -85,7 +85,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options return err }) - err = s.start(ctx, project, options.Start, printer.HandleEvent) + err = s.start(ctx, project.Name, options.Start, printer.HandleEvent) if err != nil { return err }