|  | @@ -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,11 +290,8 @@ 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
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		w.Events(containerEvents(containers, progress.Waiting))
 | 
	
		
			
				|  |  | +		waitingFor := containers.filter(isService(dep))
 | 
	
		
			
				|  |  | +		w.Events(containerEvents(waitingFor, progress.Waiting))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		dep, config := dep, config
 | 
	
		
			
				|  |  |  		eg.Go(func() error {
 | 
	
	
		
			
				|  | @@ -305,31 +301,31 @@ 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, waitingFor, true)
 | 
	
		
			
				|  |  |  					if err != nil {
 | 
	
		
			
				|  |  |  						return err
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  					if healthy {
 | 
	
		
			
				|  |  | -						w.Events(containerEvents(containers, progress.Healthy))
 | 
	
		
			
				|  |  | +						w.Events(containerEvents(waitingFor, progress.Healthy))
 | 
	
		
			
				|  |  |  						return nil
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  				case types.ServiceConditionHealthy:
 | 
	
		
			
				|  |  | -					healthy, err := s.isServiceHealthy(ctx, project, dep, false)
 | 
	
		
			
				|  |  | +					healthy, err := s.isServiceHealthy(ctx, waitingFor, false)
 | 
	
		
			
				|  |  |  					if err != nil {
 | 
	
		
			
				|  |  | -						w.Events(containerEvents(containers, progress.ErrorEvent))
 | 
	
		
			
				|  |  | +						w.Events(containerEvents(waitingFor, progress.ErrorEvent))
 | 
	
		
			
				|  |  |  						return errors.Wrap(err, "dependency failed to start")
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  					if healthy {
 | 
	
		
			
				|  |  | -						w.Events(containerEvents(containers, progress.Healthy))
 | 
	
		
			
				|  |  | +						w.Events(containerEvents(waitingFor, progress.Healthy))
 | 
	
		
			
				|  |  |  						return nil
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  				case types.ServiceConditionCompletedSuccessfully:
 | 
	
		
			
				|  |  | -					exited, code, err := s.isServiceCompleted(ctx, project, dep)
 | 
	
		
			
				|  |  | +					exited, code, err := s.isServiceCompleted(ctx, waitingFor)
 | 
	
		
			
				|  |  |  					if err != nil {
 | 
	
		
			
				|  |  |  						return err
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  					if exited {
 | 
	
		
			
				|  |  | -						w.Events(containerEvents(containers, progress.Exited))
 | 
	
		
			
				|  |  | +						w.Events(containerEvents(waitingFor, progress.Exited))
 | 
	
		
			
				|  |  |  						if code != 0 {
 | 
	
		
			
				|  |  |  							return fmt.Errorf("service %q didn't complete successfully: exit %d", dep, code)
 | 
	
		
			
				|  |  |  						}
 | 
	
	
		
			
				|  | @@ -650,51 +646,41 @@ 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
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	if len(containers) == 0 {
 | 
	
		
			
				|  |  | -		return false, nil
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | +func (s *composeService) isServiceHealthy(ctx context.Context, containers Containers, fallbackRunning bool) (bool, error) {
 | 
	
		
			
				|  |  |  	for _, c := range containers {
 | 
	
		
			
				|  |  |  		container, err := s.apiClient().ContainerInspect(ctx, c.ID)
 | 
	
		
			
				|  |  |  		if err != nil {
 | 
	
		
			
				|  |  |  			return false, err
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | +		name := container.Name[1:]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if container.State.Status == "exited" {
 | 
	
		
			
				|  |  | +			return false, fmt.Errorf("container %s exited (%d)", name, container.State.ExitCode)
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		if container.Config.Healthcheck == nil && fallbackRunning {
 | 
	
		
			
				|  |  |  			// Container does not define a health check, but we can fall back to "running" state
 | 
	
		
			
				|  |  |  			return container.State != nil && container.State.Status == "running", nil
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if container.State.Status == "exited" {
 | 
	
		
			
				|  |  | -			return false, fmt.Errorf("container for service %q exited (%d)", service, container.State.ExitCode)
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  		if container.State == nil || container.State.Health == nil {
 | 
	
		
			
				|  |  | -			return false, fmt.Errorf("container for service %q has no healthcheck configured", service)
 | 
	
		
			
				|  |  | +			return false, fmt.Errorf("container %s has no healthcheck configured", name)
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  		switch container.State.Health.Status {
 | 
	
		
			
				|  |  |  		case moby.Healthy:
 | 
	
		
			
				|  |  |  			// Continue by checking the next container.
 | 
	
		
			
				|  |  |  		case moby.Unhealthy:
 | 
	
		
			
				|  |  | -			return false, fmt.Errorf("container for service %q is unhealthy", service)
 | 
	
		
			
				|  |  | +			return false, fmt.Errorf("container %s is unhealthy", name)
 | 
	
		
			
				|  |  |  		case moby.Starting:
 | 
	
		
			
				|  |  |  			return false, nil
 | 
	
		
			
				|  |  |  		default:
 | 
	
		
			
				|  |  | -			return false, fmt.Errorf("container for service %q had unexpected health status %q", service, container.State.Health.Status)
 | 
	
		
			
				|  |  | +			return false, fmt.Errorf("container %s had unexpected health status %q", name, container.State.Health.Status)
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	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) (bool, int, error) {
 | 
	
		
			
				|  |  |  	for _, c := range containers {
 | 
	
		
			
				|  |  |  		container, err := s.apiClient().ContainerInspect(ctx, c.ID)
 | 
	
		
			
				|  |  |  		if err != nil {
 | 
	
	
		
			
				|  | @@ -707,23 +693,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
 | 
	
		
			
				|  |  |  	}
 | 
	
	
		
			
				|  | @@ -736,7 +711,7 @@ func (s *composeService) startService(ctx context.Context, project *types.Projec
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	w := progress.ContextWriter(ctx)
 | 
	
		
			
				|  |  | -	for _, container := range containers {
 | 
	
		
			
				|  |  | +	for _, container := range containers.filter(isService(service.Name)) {
 | 
	
		
			
				|  |  |  		if container.State == ContainerRunning {
 | 
	
		
			
				|  |  |  			continue
 | 
	
		
			
				|  |  |  		}
 |