diff --git a/formatter/logs.go b/formatter/logs.go index b70b9f458..c3542bcd1 100644 --- a/formatter/logs.go +++ b/formatter/logs.go @@ -48,7 +48,7 @@ func (l *logConsumer) Log(service, container, message string) { l.colors[service] = cf l.computeWidth() } - prefix := fmt.Sprintf("%-"+strconv.Itoa(l.width)+"s |", service) + prefix := fmt.Sprintf("%-"+strconv.Itoa(l.width)+"s |", container) for _, line := range strings.Split(message, "\n") { buf := bytes.NewBufferString(fmt.Sprintf("%s %s\n", cf(prefix), line)) diff --git a/local/compose/attach.go b/local/compose/attach.go index 3866cf9af..47f0c8cf8 100644 --- a/local/compose/attach.go +++ b/local/compose/attach.go @@ -61,7 +61,7 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, con func (s *composeService) attachContainer(ctx context.Context, container moby.Container, consumer compose.LogConsumer, project *types.Project) error { serviceName := container.Labels[serviceLabel] - w := getWriter(serviceName, container.ID, consumer) + w := getWriter(serviceName, getContainerName(container), consumer) service, err := project.GetService(serviceName) if err != nil { diff --git a/local/compose/convergence.go b/local/compose/convergence.go index 4721a8ab0..89d460424 100644 --- a/local/compose/convergence.go +++ b/local/compose/convergence.go @@ -26,6 +26,7 @@ import ( moby "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" + "github.com/docker/docker/client" "golang.org/x/sync/errgroup" status "github.com/docker/compose-cli/local/moby" @@ -35,11 +36,25 @@ import ( const ( extLifecycle = "x-lifecycle" forceRecreate = "force_recreate" + + doubledContainerNameWarning = "WARNING: The %q service is using the custom container name %q. " + + "Docker requires each container to have a unique name. " + + "Remove the custom name to scale the service.\n" ) +func containerExists(ctx context.Context, c *client.Client, name string) bool { + container, err := c.ContainerInspect(ctx, name) + return err == nil && container.ContainerJSONBase != nil && container.Name == "/"+name +} + func (s *composeService) ensureService(ctx context.Context, observedState Containers, project *types.Project, service types.ServiceConfig) error { scale := getScale(service) actual := observedState.filter(isService(service.Name)) + if scale > 1 && service.ContainerName != "" { + return fmt.Errorf(doubledContainerNameWarning, + service.Name, + service.ContainerName) + } eg, _ := errgroup.WithContext(ctx) if len(actual) < scale { @@ -50,8 +65,13 @@ func (s *composeService) ensureService(ctx context.Context, observedState Contai missing := scale - len(actual) for i := 0; i < missing; i++ { number := next + i - name := fmt.Sprintf("%s_%s_%d", project.Name, service.Name, number) + name := getContainerLogPrefix(project.Name, service, number) eg.Go(func() error { + if containerExists(ctx, s.apiClient, name) { + return fmt.Errorf(doubledContainerNameWarning, + service.Name, + name) + } return s.createContainer(ctx, project, service, name, number, false) }) } @@ -104,6 +124,14 @@ func (s *composeService) ensureService(ctx context.Context, observedState Contai return eg.Wait() } +func getContainerLogPrefix(projectName string, service types.ServiceConfig, number int) string { + name := fmt.Sprintf("%s_%s_%d", projectName, service.Name, number) + if service.ContainerName != "" { + name = service.ContainerName + } + return name +} + func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, service types.ServiceConfig) error { eg, _ := errgroup.WithContext(ctx) for dep, config := range service.DependsOn {