Merge pull request #1079 from docker/container_name

Use `container_name` property on service
This commit is contained in:
Guillaume Tardif 2021-01-08 14:26:33 +01:00 committed by GitHub
commit bf5b8f9b8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 124 additions and 35 deletions

View File

@ -48,7 +48,7 @@ func (l *logConsumer) Log(service, container, message string) {
l.colors[service] = cf l.colors[service] = cf
l.computeWidth() 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") { for _, line := range strings.Split(message, "\n") {
buf := bytes.NewBufferString(fmt.Sprintf("%s %s\n", cf(prefix), line)) buf := bytes.NewBufferString(fmt.Sprintf("%s %s\n", cf(prefix), line))

View File

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

View File

@ -25,10 +25,11 @@ import (
"github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/compose"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
errdefs2 "github.com/docker/compose-cli/errdefs"
moby "github.com/docker/docker/api/types" moby "github.com/docker/docker/api/types"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/sanathkr/go-yaml" "github.com/sanathkr/go-yaml"
errdefs2 "github.com/docker/compose-cli/errdefs"
) )
// NewComposeService create a local implementation of the compose.Service API // 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 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 // Names return container canonical name /foo + link aliases /linked_by/foo
for _, name := range c.Names { for _, name := range c.Names {
if strings.LastIndex(name, "/") == 0 { if strings.LastIndex(name, "/") == 0 {

View File

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

View File

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

View File

@ -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))
}

View File

@ -213,7 +213,7 @@ func getCreateOptions(p *types.Project, s types.ServiceConfig, number int, inher
Resources: resources, Resources: resources,
} }
networkConfig := buildDefaultNetworkConfig(s, networkMode) networkConfig := buildDefaultNetworkConfig(s, networkMode, getContainerName(p.Name, s, number))
return &containerConfig, &hostConfig, networkConfig, nil 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{} config := map[string]*network.EndpointSettings{}
net := string(networkMode) net := string(networkMode)
config[net] = &network.EndpointSettings{ config[net] = &network.EndpointSettings{
Aliases: getAliases(s, s.Networks[net]), Aliases: append(getAliases(s, s.Networks[net]), containerName),
} }
return &network.NetworkingConfig{ return &network.NetworkingConfig{

View File

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

View File

@ -21,9 +21,10 @@ import (
"fmt" "fmt"
"sort" "sort"
"github.com/docker/compose-cli/api/compose"
moby "github.com/docker/docker/api/types" moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "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) { 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{ summary = append(summary, compose.ContainerSummary{
ID: c.ID, ID: c.ID,
Name: getContainerName(c), Name: getCanonicalContainerName(c),
Project: c.Labels[projectLabel], Project: c.Labels[projectLabel],
Service: c.Labels[serviceLabel], Service: c.Labels[serviceLabel],
State: c.State, State: c.State,