mirror of https://github.com/docker/compose.git
Merge pull request #1079 from docker/container_name
Use `container_name` property on service
This commit is contained in:
commit
bf5b8f9b8a
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -35,22 +35,23 @@ 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 (s *composeService) ensureService(ctx context.Context, observedState Containers, project *types.Project, service types.ServiceConfig) error {
|
||||
scale := getScale(service)
|
||||
actual := observedState.filter(isService(service.Name))
|
||||
|
||||
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
|
||||
}
|
||||
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 {
|
||||
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue