package daemon // import "github.com/docker/docker/daemon" import ( "context" "fmt" "strings" "github.com/containerd/log" "github.com/docker/docker/api/types/events" dockercontainer "github.com/docker/docker/container" "github.com/docker/docker/daemon/network" "github.com/docker/docker/errdefs" "github.com/docker/docker/libnetwork" "github.com/pkg/errors" ) // ContainerRename changes the name of a container, using the oldName // to find the container. An error is returned if newName is already // reserved. func (daemon *Daemon) ContainerRename(oldName, newName string) (retErr error) { if oldName == "" || newName == "" { return errdefs.InvalidParameter(errors.New("Neither old nor new names may be empty")) } container, err := daemon.GetContainer(oldName) if err != nil { return err } container.Lock() defer container.Unlock() // Canonicalize name for comparing. if newName[0] != '/' { newName = "/" + newName } if container.Name == newName { return errdefs.InvalidParameter(errors.New("Renaming a container with the same name as its current name")) } links := map[string]*dockercontainer.Container{} for k, v := range daemon.linkIndex.children(container) { if !strings.HasPrefix(k, container.Name) { return errdefs.InvalidParameter(errors.Errorf("Linked container %s does not match parent %s", k, container.Name)) } links[strings.TrimPrefix(k, container.Name)] = v } newName, err = daemon.reserveName(container.ID, newName) if err != nil { return errors.Wrap(err, "Error when allocating new name") } for k, v := range links { daemon.containersReplica.ReserveName(newName+k, v.ID) daemon.linkIndex.link(container, v, newName+k) } oldName = container.Name container.Name = newName defer func() { if retErr != nil { container.Name = oldName daemon.reserveName(container.ID, oldName) for k, v := range links { daemon.containersReplica.ReserveName(oldName+k, v.ID) daemon.linkIndex.link(container, v, oldName+k) daemon.linkIndex.unlink(newName+k, v, container) daemon.containersReplica.ReleaseName(newName + k) } daemon.releaseName(newName) } else { daemon.releaseName(oldName) } }() for k, v := range links { daemon.linkIndex.unlink(oldName+k, v, container) daemon.containersReplica.ReleaseName(oldName + k) } if err := container.CheckpointTo(daemon.containersReplica); err != nil { return err } if !container.Running { daemon.LogContainerEventWithAttributes(container, events.ActionRename, map[string]string{ "oldName": oldName, }) return nil } defer func() { if retErr != nil { container.Name = oldName if err := container.CheckpointTo(daemon.containersReplica); err != nil { log.G(context.TODO()).WithFields(log.Fields{ "containerID": container.ID, "error": err, }).Error("failed to write container state to disk during rename") } } }() if sid := container.NetworkSettings.SandboxID; sid != "" && daemon.netController != nil { sb, err := daemon.netController.SandboxByID(sid) if err != nil { return err } if err = sb.Rename(strings.TrimPrefix(container.Name, "/")); err != nil { return err } defer func() { if retErr != nil { if err := sb.Rename(oldName); err != nil { log.G(context.TODO()).WithFields(log.Fields{ "sandboxID": sid, "oldName": oldName, "newName": newName, "error": err, }).Errorf("failed to revert sandbox rename") } } }() for nwName, epConfig := range container.NetworkSettings.Networks { nw, err := daemon.FindNetwork(nwName) if err != nil { return err } ep := sb.GetEndpoint(epConfig.EndpointID) if ep == nil { return fmt.Errorf("no endpoint attached to network %s found", nw.Name()) } oldDNSNames := make([]string, len(epConfig.DNSNames)) copy(oldDNSNames, epConfig.DNSNames) epConfig.DNSNames = buildEndpointDNSNames(container, epConfig.Aliases) if err := ep.UpdateDNSNames(epConfig.DNSNames); err != nil { return err } defer func(ep *libnetwork.Endpoint, epConfig *network.EndpointSettings, oldDNSNames []string) { if retErr == nil { return } epConfig.DNSNames = oldDNSNames if err := ep.UpdateDNSNames(epConfig.DNSNames); err != nil { log.G(context.TODO()).WithFields(log.Fields{ "sandboxID": sid, "oldName": oldName, "newName": newName, "error": err, }).Errorf("failed to revert DNSNames update") } }(ep, epConfig, oldDNSNames) } } daemon.LogContainerEventWithAttributes(container, events.ActionRename, map[string]string{ "oldName": oldName, }) return nil }