Bladeren bron

Merge pull request #1079 from docker/container_name

Use `container_name` property on service
Guillaume Tardif 4 jaren geleden
bovenliggende
commit
bf5b8f9b8a

+ 1 - 1
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))

+ 2 - 2
local/compose/attach.go

@@ -45,7 +45,7 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, con
 
 	var names []string
 	for _, c := range containers {
-		names = append(names, getContainerName(c))
+		names = append(names, getCanonicalContainerName(c))
 	}
 	fmt.Printf("Attaching to %s\n", strings.Join(names, ", "))
 
@@ -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, getCanonicalContainerName(container), consumer)
 
 	service, err := project.GetService(serviceName)
 	if err != nil {

+ 3 - 2
local/compose/compose.go

@@ -25,10 +25,11 @@ import (
 	"github.com/docker/compose-cli/api/compose"
 
 	"github.com/compose-spec/compose-go/types"
-	errdefs2 "github.com/docker/compose-cli/errdefs"
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
 	"github.com/sanathkr/go-yaml"
+
+	errdefs2 "github.com/docker/compose-cli/errdefs"
 )
 
 // NewComposeService create a local implementation of the compose.Service API
@@ -44,7 +45,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
 	return errdefs2.ErrNotImplemented
 }
 
-func getContainerName(c moby.Container) string {
+func getCanonicalContainerName(c moby.Container) string {
 	// Names return container canonical name /foo  + link aliases /linked_by/foo
 	for _, name := range c.Names {
 		if strings.LastIndex(name, "/") == 0 {

+ 1 - 1
local/compose/containers.go

@@ -66,7 +66,7 @@ func (containers Containers) split(predicate containerPredicate) (Containers, Co
 func (containers Containers) names() []string {
 	var names []string
 	for _, c := range containers {
-		names = append(names, getContainerName(c))
+		names = append(names, getCanonicalContainerName(c))
 	}
 	return names
 }

+ 55 - 23
local/compose/convergence.go

@@ -35,22 +35,23 @@ import (
 const (
 	extLifecycle  = "x-lifecycle"
 	forceRecreate = "force_recreate"
-)
 
-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))
+	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 (s *composeService) ensureScale(ctx context.Context, actual []moby.Container, scale int, project *types.Project, service types.ServiceConfig) (*errgroup.Group, []moby.Container, error) {
 	eg, _ := errgroup.WithContext(ctx)
 	if len(actual) < scale {
 		next, err := nextContainerNumber(actual)
 		if err != nil {
-			return err
+			return nil, actual, err
 		}
 		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 := getContainerName(project.Name, service, number)
 			eg.Go(func() error {
 				return s.createContainer(ctx, project, service, name, number, false)
 			})
@@ -70,6 +71,21 @@ func (s *composeService) ensureService(ctx context.Context, observedState Contai
 		}
 		actual = actual[:scale]
 	}
+	return eg, actual, nil
+}
+
+func (s *composeService) ensureService(ctx context.Context, observedState Containers, project *types.Project, service types.ServiceConfig) error {
+	actual := observedState.filter(isService(service.Name))
+
+	scale, err := getScale(service)
+	if err != nil {
+		return err
+	}
+
+	eg, actual, err := s.ensureScale(ctx, actual, scale, project, service)
+	if err != nil {
+		return err
+	}
 
 	expected, err := jsonHash(service)
 	if err != nil {
@@ -78,7 +94,7 @@ func (s *composeService) ensureService(ctx context.Context, observedState Contai
 
 	for _, container := range actual {
 		container := container
-		name := getContainerName(container)
+		name := getCanonicalContainerName(container)
 
 		diverged := container.Labels[configHashLabel] != expected
 		if diverged || service.Extensions[extLifecycle] == forceRecreate {
@@ -104,6 +120,14 @@ func (s *composeService) ensureService(ctx context.Context, observedState Contai
 	return eg.Wait()
 }
 
+func getContainerName(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 {
@@ -143,14 +167,22 @@ func nextContainerNumber(containers []moby.Container) (int, error) {
 
 }
 
-func getScale(config types.ServiceConfig) int {
+func getScale(config types.ServiceConfig) (int, error) {
+	scale := 1
+	var err error
 	if config.Deploy != nil && config.Deploy.Replicas != nil {
-		return int(*config.Deploy.Replicas)
+		scale = int(*config.Deploy.Replicas)
 	}
 	if config.Scale != 0 {
-		return config.Scale
+		scale = config.Scale
+	}
+	if scale > 1 && config.ContainerName != "" {
+		scale = -1
+		err = fmt.Errorf(doubledContainerNameWarning,
+			config.Name,
+			config.ContainerName)
 	}
-	return 1
+	return scale, err
 }
 
 func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, autoRemove bool) error {
@@ -166,12 +198,12 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
 
 func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, container moby.Container) error {
 	w := progress.ContextWriter(ctx)
-	w.Event(progress.NewEvent(getContainerName(container), progress.Working, "Recreate"))
+	w.Event(progress.NewEvent(getCanonicalContainerName(container), progress.Working, "Recreate"))
 	err := s.apiClient.ContainerStop(ctx, container.ID, nil)
 	if err != nil {
 		return err
 	}
-	name := getContainerName(container)
+	name := getCanonicalContainerName(container)
 	tmpName := fmt.Sprintf("%s_%s", container.ID[:12], name)
 	err = s.apiClient.ContainerRename(ctx, container.ID, tmpName)
 	if err != nil {
@@ -189,7 +221,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
 	if err != nil {
 		return err
 	}
-	w.Event(progress.NewEvent(getContainerName(container), progress.Done, "Recreated"))
+	w.Event(progress.NewEvent(getCanonicalContainerName(container), progress.Done, "Recreated"))
 	setDependentLifecycle(project, service.Name, forceRecreate)
 	return nil
 }
@@ -209,12 +241,12 @@ func setDependentLifecycle(project *types.Project, service string, strategy stri
 
 func (s *composeService) restartContainer(ctx context.Context, container moby.Container) error {
 	w := progress.ContextWriter(ctx)
-	w.Event(progress.NewEvent(getContainerName(container), progress.Working, "Restart"))
+	w.Event(progress.NewEvent(getCanonicalContainerName(container), progress.Working, "Restart"))
 	err := s.apiClient.ContainerStart(ctx, container.ID, moby.ContainerStartOptions{})
 	if err != nil {
 		return err
 	}
-	w.Event(progress.NewEvent(getContainerName(container), progress.Done, "Restarted"))
+	w.Event(progress.NewEvent(getCanonicalContainerName(container), progress.Done, "Restarted"))
 	return nil
 }
 
@@ -229,8 +261,8 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
 	}
 	id := created.ID
 	for netName := range service.Networks {
-		network := project.Networks[netName]
-		err = s.connectContainerToNetwork(ctx, id, service.Name, network.Name)
+		netwrk := project.Networks[netName]
+		err = s.connectContainerToNetwork(ctx, id, netwrk.Name, service.Name, getContainerName(project.Name, service, number))
 		if err != nil {
 			return err
 		}
@@ -238,9 +270,9 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
 	return nil
 }
 
-func (s *composeService) connectContainerToNetwork(ctx context.Context, id string, service string, n string) error {
-	err := s.apiClient.NetworkConnect(ctx, n, id, &network.EndpointSettings{
-		Aliases: []string{service},
+func (s *composeService) connectContainerToNetwork(ctx context.Context, id string, netwrk string, aliases ...string) error {
+	err := s.apiClient.NetworkConnect(ctx, netwrk, id, &network.EndpointSettings{
+		Aliases: aliases,
 	})
 	if err != nil {
 		return err
@@ -300,10 +332,10 @@ func (s *composeService) startService(ctx context.Context, project *types.Projec
 		}
 		eg.Go(func() error {
 			w := progress.ContextWriter(ctx)
-			w.Event(progress.StartingEvent(getContainerName(container)))
+			w.Event(progress.StartingEvent(getCanonicalContainerName(container)))
 			err := s.apiClient.ContainerStart(ctx, container.ID, moby.ContainerStartOptions{})
 			if err == nil {
-				w.Event(progress.StartedEvent(getContainerName(container)))
+				w.Event(progress.StartedEvent(getCanonicalContainerName(container)))
 			}
 			return err
 		})

+ 55 - 0
local/compose/convergence_test.go

@@ -0,0 +1,55 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/compose-spec/compose-go/types"
+	"gotest.tools/assert"
+)
+
+func TestContainerName(t *testing.T) {
+	var replicas uint64 = 1
+	s := types.ServiceConfig{
+		Name:          "testservicename",
+		ContainerName: "testcontainername",
+		Scale:         1,
+		Deploy:        &types.DeployConfig{},
+	}
+	ret, err := getScale(s)
+	assert.NilError(t, err)
+	assert.Equal(t, ret, s.Scale)
+
+	s.Scale = 0
+	s.Deploy.Replicas = &replicas
+	ret, err = getScale(s)
+	assert.NilError(t, err)
+	assert.Equal(t, ret, int(*s.Deploy.Replicas))
+
+	s.Deploy.Replicas = nil
+	s.Scale = 2
+	_, err = getScale(s)
+	assert.Error(t, err, fmt.Sprintf(doubledContainerNameWarning, s.Name, s.ContainerName))
+
+	replicas = 2
+	s.Deploy.Replicas = &replicas
+	s.Scale = 0
+	_, err = getScale(s)
+	assert.Error(t, err, fmt.Sprintf(doubledContainerNameWarning, s.Name, s.ContainerName))
+}

+ 3 - 3
local/compose/create.go

@@ -213,7 +213,7 @@ func getCreateOptions(p *types.Project, s types.ServiceConfig, number int, inher
 		Resources:    resources,
 	}
 
-	networkConfig := buildDefaultNetworkConfig(s, networkMode)
+	networkConfig := buildDefaultNetworkConfig(s, networkMode, getContainerName(p.Name, s, number))
 	return &containerConfig, &hostConfig, networkConfig, nil
 }
 
@@ -359,11 +359,11 @@ func buildTmpfsOptions(tmpfs *types.ServiceVolumeTmpfs) *mount.TmpfsOptions {
 	}
 }
 
-func buildDefaultNetworkConfig(s types.ServiceConfig, networkMode container.NetworkMode) *network.NetworkingConfig {
+func buildDefaultNetworkConfig(s types.ServiceConfig, networkMode container.NetworkMode, containerName string) *network.NetworkingConfig {
 	config := map[string]*network.EndpointSettings{}
 	net := string(networkMode)
 	config[net] = &network.EndpointSettings{
-		Aliases: getAliases(s, s.Networks[net]),
+		Aliases: append(getAliases(s, s.Networks[net]), containerName),
 	}
 
 	return &network.NetworkingConfig{

+ 1 - 1
local/compose/down.go

@@ -94,7 +94,7 @@ func (s *composeService) removeContainers(ctx context.Context, w progress.Writer
 	for _, container := range containers {
 		toDelete := container
 		eg.Go(func() error {
-			eventName := "Container " + getContainerName(toDelete)
+			eventName := "Container " + getCanonicalContainerName(toDelete)
 			w.Event(progress.StoppingEvent(eventName))
 			err := s.apiClient.ContainerStop(ctx, toDelete.ID, nil)
 			if err != nil {

+ 3 - 2
local/compose/ps.go

@@ -21,9 +21,10 @@ import (
 	"fmt"
 	"sort"
 
-	"github.com/docker/compose-cli/api/compose"
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
+
+	"github.com/docker/compose-cli/api/compose"
 )
 
 func (s *composeService) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) {
@@ -54,7 +55,7 @@ func (s *composeService) Ps(ctx context.Context, projectName string) ([]compose.
 
 		summary = append(summary, compose.ContainerSummary{
 			ID:         c.ID,
-			Name:       getContainerName(c),
+			Name:       getCanonicalContainerName(c),
 			Project:    c.Labels[projectLabel],
 			Service:    c.Labels[serviceLabel],
 			State:      c.State,