|  | @@ -45,9 +45,6 @@ import (
 | 
											
												
													
														|  |  )
 |  |  )
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  const (
 |  |  const (
 | 
											
												
													
														|  | -	extLifecycle  = "x-lifecycle"
 |  | 
 | 
											
												
													
														|  | -	forceRecreate = "force_recreate"
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |  	doubledContainerNameWarning = "WARNING: The %q service is using the custom container name %q. " +
 |  |  	doubledContainerNameWarning = "WARNING: The %q service is using the custom container name %q. " +
 | 
											
												
													
														|  |  		"Docker requires each container to have a unique name. " +
 |  |  		"Docker requires each container to have a unique name. " +
 | 
											
												
													
														|  |  		"Remove the custom name to scale the service.\n"
 |  |  		"Remove the custom name to scale the service.\n"
 | 
											
										
											
												
													
														|  | @@ -108,9 +105,7 @@ func (c *convergence) apply(ctx context.Context, project *types.Project, options
 | 
											
												
													
														|  |  	})
 |  |  	})
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -var mu sync.Mutex
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -func (c *convergence) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error {
 |  | 
 | 
											
												
													
														|  | 
 |  | +func (c *convergence) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error { //nolint:gocyclo
 | 
											
												
													
														|  |  	expected, err := getScale(service)
 |  |  	expected, err := getScale(service)
 | 
											
												
													
														|  |  	if err != nil {
 |  |  	if err != nil {
 | 
											
												
													
														|  |  		return err
 |  |  		return err
 | 
											
										
											
												
													
														|  | @@ -147,6 +142,7 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
 | 
											
												
													
														|  |  		// If we don't get a container number (?) just sort by creation date
 |  |  		// If we don't get a container number (?) just sort by creation date
 | 
											
												
													
														|  |  		return containers[i].Created < containers[j].Created
 |  |  		return containers[i].Created < containers[j].Created
 | 
											
												
													
														|  |  	})
 |  |  	})
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  	for i, container := range containers {
 |  |  	for i, container := range containers {
 | 
											
												
													
														|  |  		if i >= expected {
 |  |  		if i >= expected {
 | 
											
												
													
														|  |  			// Scale Down
 |  |  			// Scale Down
 | 
											
										
											
												
													
														|  | @@ -163,6 +159,11 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
 | 
											
												
													
														|  |  			return err
 |  |  			return err
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  |  		if mustRecreate {
 |  |  		if mustRecreate {
 | 
											
												
													
														|  | 
 |  | +			err := c.stopDependentContainers(ctx, project, service)
 | 
											
												
													
														|  | 
 |  | +			if err != nil {
 | 
											
												
													
														|  | 
 |  | +				return err
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  			i, container := i, container
 |  |  			i, container := i, container
 | 
											
												
													
														|  |  			eg.Go(tracing.SpanWrapFuncForErrGroup(ctx, "container/recreate", tracing.ContainerOptions(container), func(ctx context.Context) error {
 |  |  			eg.Go(tracing.SpanWrapFuncForErrGroup(ctx, "container/recreate", tracing.ContainerOptions(container), func(ctx context.Context) error {
 | 
											
												
													
														|  |  				recreated, err := c.service.recreateContainer(ctx, project, service, container, inherit, timeout)
 |  |  				recreated, err := c.service.recreateContainer(ctx, project, service, container, inherit, timeout)
 | 
											
										
											
												
													
														|  | @@ -217,6 +218,25 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
 | 
											
												
													
														|  |  	return err
 |  |  	return err
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +func (c *convergence) stopDependentContainers(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
 | 
											
												
													
														|  | 
 |  | +	w := progress.ContextWriter(ctx)
 | 
											
												
													
														|  | 
 |  | +	// Stop dependent containers, so they will be restarted after service is re-created
 | 
											
												
													
														|  | 
 |  | +	dependents := project.GetDependentsForService(service)
 | 
											
												
													
														|  | 
 |  | +	for _, name := range dependents {
 | 
											
												
													
														|  | 
 |  | +		dependents := c.observedState[name]
 | 
											
												
													
														|  | 
 |  | +		err := c.service.stopContainers(ctx, w, dependents, nil)
 | 
											
												
													
														|  | 
 |  | +		if err != nil {
 | 
											
												
													
														|  | 
 |  | +			return err
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +		for i, dependent := range dependents {
 | 
											
												
													
														|  | 
 |  | +			dependent.State = ContainerExited
 | 
											
												
													
														|  | 
 |  | +			dependents[i] = dependent
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +		c.observedState[name] = dependents
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  | 
 |  | +	return nil
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  func getScale(config types.ServiceConfig) (int, error) {
 |  |  func getScale(config types.ServiceConfig) (int, error) {
 | 
											
												
													
														|  |  	scale := config.GetScale()
 |  |  	scale := config.GetScale()
 | 
											
												
													
														|  |  	if scale > 1 && config.ContainerName != "" {
 |  |  	if scale > 1 && config.ContainerName != "" {
 | 
											
										
											
												
													
														|  | @@ -296,7 +316,7 @@ func mustRecreate(expected types.ServiceConfig, actual moby.Container, policy st
 | 
											
												
													
														|  |  	if policy == api.RecreateNever {
 |  |  	if policy == api.RecreateNever {
 | 
											
												
													
														|  |  		return false, nil
 |  |  		return false, nil
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  | -	if policy == api.RecreateForce || expected.Extensions[extLifecycle] == forceRecreate {
 |  | 
 | 
											
												
													
														|  | 
 |  | +	if policy == api.RecreateForce {
 | 
											
												
													
														|  |  		return true, nil
 |  |  		return true, nil
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  	configHash, err := ServiceHash(expected)
 |  |  	configHash, err := ServiceHash(expected)
 | 
											
										
											
												
													
														|  | @@ -535,26 +555,9 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Done, "Recreated"))
 |  |  	w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Done, "Recreated"))
 | 
											
												
													
														|  | -	setDependentLifecycle(project, service.Name, forceRecreate)
 |  | 
 | 
											
												
													
														|  |  	return created, err
 |  |  	return created, err
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -// setDependentLifecycle define the Lifecycle strategy for all services to depend on specified service
 |  | 
 | 
											
												
													
														|  | -func setDependentLifecycle(project *types.Project, service string, strategy string) {
 |  | 
 | 
											
												
													
														|  | -	mu.Lock()
 |  | 
 | 
											
												
													
														|  | -	defer mu.Unlock()
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -	for i, s := range project.Services {
 |  | 
 | 
											
												
													
														|  | -		if utils.StringContains(s.GetDependencies(), service) {
 |  | 
 | 
											
												
													
														|  | -			if s.Extensions == nil {
 |  | 
 | 
											
												
													
														|  | -				s.Extensions = map[string]interface{}{}
 |  | 
 | 
											
												
													
														|  | -			}
 |  | 
 | 
											
												
													
														|  | -			s.Extensions[extLifecycle] = strategy
 |  | 
 | 
											
												
													
														|  | -			project.Services[i] = s
 |  | 
 | 
											
												
													
														|  | -		}
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | -}
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |  func (s *composeService) startContainer(ctx context.Context, container moby.Container) error {
 |  |  func (s *composeService) startContainer(ctx context.Context, container moby.Container) error {
 | 
											
												
													
														|  |  	w := progress.ContextWriter(ctx)
 |  |  	w := progress.ContextWriter(ctx)
 | 
											
												
													
														|  |  	w.Event(progress.NewEvent(getContainerProgressName(container), progress.Working, "Restart"))
 |  |  	w.Event(progress.NewEvent(getContainerProgressName(container), progress.Working, "Restart"))
 |