docker/docker

View on GitHub
daemon/rename.go

Summary

Maintainability
C
1 day
Test Coverage
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
}