diff --git a/pkg/compose/convergence.go b/pkg/compose/convergence.go index ea27e9bcd..333ef70e3 100644 --- a/pkg/compose/convergence.go +++ b/pkg/compose/convergence.go @@ -28,7 +28,6 @@ import ( "github.com/containerd/containerd/platforms" moby "github.com/docker/docker/api/types" containerType "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -281,7 +280,7 @@ func containerEvents(containers Containers, eventFunc func(string) progress.Even // ServiceConditionRunningOrHealthy is a service condition on status running or healthy const ServiceConditionRunningOrHealthy = "running_or_healthy" -func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependencies types.DependsOnConfig) error { +func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependencies types.DependsOnConfig, containers Containers) error { eg, _ := errgroup.WithContext(ctx) w := progress.ContextWriter(ctx) for dep, config := range dependencies { @@ -291,10 +290,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr continue } - containers, err := s.getContainers(ctx, project.Name, oneOffExclude, false, dep) - if err != nil { - return err - } + containers = containers.filter(isService(dep)) w.Events(containerEvents(containers, progress.Waiting)) dep, config := dep, config @@ -305,7 +301,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr <-ticker.C switch config.Condition { case ServiceConditionRunningOrHealthy: - healthy, err := s.isServiceHealthy(ctx, project, dep, true) + healthy, err := s.isServiceHealthy(ctx, containers, dep, true) if err != nil { return err } @@ -314,7 +310,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr return nil } case types.ServiceConditionHealthy: - healthy, err := s.isServiceHealthy(ctx, project, dep, false) + healthy, err := s.isServiceHealthy(ctx, containers, dep, false) if err != nil { w.Events(containerEvents(containers, progress.ErrorEvent)) return errors.Wrap(err, "dependency failed to start") @@ -324,7 +320,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr return nil } case types.ServiceConditionCompletedSuccessfully: - exited, code, err := s.isServiceCompleted(ctx, project, dep) + exited, code, err := s.isServiceCompleted(ctx, containers, dep) if err != nil { return err } @@ -650,12 +646,7 @@ func (s *composeService) connectContainerToNetwork(ctx context.Context, id strin return nil } -func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Project, service string, fallbackRunning bool) (bool, error) { - containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, service) - if err != nil { - return false, err - } - +func (s *composeService) isServiceHealthy(ctx context.Context, containers Containers, service string, fallbackRunning bool) (bool, error) { if len(containers) == 0 { return false, nil } @@ -690,11 +681,7 @@ func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Pr return true, nil } -func (s *composeService) isServiceCompleted(ctx context.Context, project *types.Project, dep string) (bool, int, error) { - containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, dep) - if err != nil { - return false, 0, err - } +func (s *composeService) isServiceCompleted(ctx context.Context, containers Containers, dep string) (bool, int, error) { for _, c := range containers { container, err := s.apiClient().ContainerInspect(ctx, c.ID) if err != nil { @@ -707,23 +694,12 @@ func (s *composeService) isServiceCompleted(ctx context.Context, project *types. return false, 0, nil } -func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig) error { +func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig, containers Containers) error { if service.Deploy != nil && service.Deploy.Replicas != nil && *service.Deploy.Replicas == 0 { return nil } - err := s.waitDependencies(ctx, project, service.DependsOn) - if err != nil { - return err - } - containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{ - Filters: filters.NewArgs( - projectFilter(project.Name), - serviceFilter(service.Name), - oneOffFilter(false), - ), - All: true, - }) + err := s.waitDependencies(ctx, project, service.DependsOn, containers) if err != nil { return err } diff --git a/pkg/compose/convergence_test.go b/pkg/compose/convergence_test.go index 1cf8f8b8b..ab9454dcf 100644 --- a/pkg/compose/convergence_test.go +++ b/pkg/compose/convergence_test.go @@ -229,7 +229,7 @@ func TestWaitDependencies(t *testing.T) { "db": {Condition: ServiceConditionRunningOrHealthy}, "redis": {Condition: ServiceConditionRunningOrHealthy}, } - assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies)) + assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies, nil)) }) t.Run("should skip dependencies with condition service_started", func(t *testing.T) { dbService := types.ServiceConfig{Name: "db", Scale: 1} @@ -239,6 +239,6 @@ func TestWaitDependencies(t *testing.T) { "db": {Condition: types.ServiceConditionStarted}, "redis": {Condition: types.ServiceConditionStarted}, } - assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies)) + assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies, nil)) }) } diff --git a/pkg/compose/run.go b/pkg/compose/run.go index bd16b435b..aafad5295 100644 --- a/pkg/compose/run.go +++ b/pkg/compose/run.go @@ -76,11 +76,6 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project, if err := s.ensureImagesExists(ctx, project, opts.QuietPull); err != nil { // all dependencies already checked, but might miss service img return "", err } - if !opts.NoDeps { - if err := s.waitDependencies(ctx, project, service.DependsOn); err != nil { - return "", err - } - } observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true) if err != nil { @@ -88,6 +83,11 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project, } updateServices(&service, observedState) + if !opts.NoDeps { + if err := s.waitDependencies(ctx, project, service.DependsOn, observedState); err != nil { + return "", err + } + } created, err := s.createContainer(ctx, project, service, service.ContainerName, 1, opts.AutoRemove, opts.UseNetworkAliases, opts.Interactive) if err != nil { diff --git a/pkg/compose/start.go b/pkg/compose/start.go index 09d02e42e..bd9d95162 100644 --- a/pkg/compose/start.go +++ b/pkg/compose/start.go @@ -18,6 +18,7 @@ package compose import ( "context" + "github.com/docker/docker/api/types/filters" "strings" "time" @@ -75,13 +76,25 @@ func (s *composeService) start(ctx context.Context, projectName string, options }) } - err := InDependencyOrder(ctx, project, func(c context.Context, name string) error { + var containers Containers + containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{ + Filters: filters.NewArgs( + projectFilter(project.Name), + oneOffFilter(false), + ), + All: true, + }) + if err != nil { + return err + } + + err = InDependencyOrder(ctx, project, func(c context.Context, name string) error { service, err := project.GetService(name) if err != nil { return err } - return s.startService(ctx, project, service) + return s.startService(ctx, project, service, containers.filter(isService(service.Name))) }) if err != nil { return err @@ -94,7 +107,7 @@ func (s *composeService) start(ctx context.Context, projectName string, options Condition: getDependencyCondition(s, project), } } - err = s.waitDependencies(ctx, project, depends) + err = s.waitDependencies(ctx, project, depends, containers) if err != nil { return err }