github.com/moby/docker@v26.1.3+incompatible/daemon/rename.go (about)

     1  package daemon // import "github.com/docker/docker/daemon"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/containerd/log"
     9  	"github.com/docker/docker/api/types/events"
    10  	dockercontainer "github.com/docker/docker/container"
    11  	"github.com/docker/docker/daemon/network"
    12  	"github.com/docker/docker/errdefs"
    13  	"github.com/docker/docker/libnetwork"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // ContainerRename changes the name of a container, using the oldName
    18  // to find the container. An error is returned if newName is already
    19  // reserved.
    20  func (daemon *Daemon) ContainerRename(oldName, newName string) (retErr error) {
    21  	if oldName == "" || newName == "" {
    22  		return errdefs.InvalidParameter(errors.New("Neither old nor new names may be empty"))
    23  	}
    24  
    25  	container, err := daemon.GetContainer(oldName)
    26  	if err != nil {
    27  		return err
    28  	}
    29  	container.Lock()
    30  	defer container.Unlock()
    31  
    32  	// Canonicalize name for comparing.
    33  	if newName[0] != '/' {
    34  		newName = "/" + newName
    35  	}
    36  	if container.Name == newName {
    37  		return errdefs.InvalidParameter(errors.New("Renaming a container with the same name as its current name"))
    38  	}
    39  
    40  	links := map[string]*dockercontainer.Container{}
    41  	for k, v := range daemon.linkIndex.children(container) {
    42  		if !strings.HasPrefix(k, container.Name) {
    43  			return errdefs.InvalidParameter(errors.Errorf("Linked container %s does not match parent %s", k, container.Name))
    44  		}
    45  		links[strings.TrimPrefix(k, container.Name)] = v
    46  	}
    47  
    48  	newName, err = daemon.reserveName(container.ID, newName)
    49  	if err != nil {
    50  		return errors.Wrap(err, "Error when allocating new name")
    51  	}
    52  
    53  	for k, v := range links {
    54  		daemon.containersReplica.ReserveName(newName+k, v.ID)
    55  		daemon.linkIndex.link(container, v, newName+k)
    56  	}
    57  
    58  	oldName = container.Name
    59  	container.Name = newName
    60  
    61  	defer func() {
    62  		if retErr != nil {
    63  			container.Name = oldName
    64  			daemon.reserveName(container.ID, oldName)
    65  			for k, v := range links {
    66  				daemon.containersReplica.ReserveName(oldName+k, v.ID)
    67  				daemon.linkIndex.link(container, v, oldName+k)
    68  				daemon.linkIndex.unlink(newName+k, v, container)
    69  				daemon.containersReplica.ReleaseName(newName + k)
    70  			}
    71  			daemon.releaseName(newName)
    72  		} else {
    73  			daemon.releaseName(oldName)
    74  		}
    75  	}()
    76  
    77  	for k, v := range links {
    78  		daemon.linkIndex.unlink(oldName+k, v, container)
    79  		daemon.containersReplica.ReleaseName(oldName + k)
    80  	}
    81  	if err := container.CheckpointTo(daemon.containersReplica); err != nil {
    82  		return err
    83  	}
    84  
    85  	if !container.Running {
    86  		daemon.LogContainerEventWithAttributes(container, events.ActionRename, map[string]string{
    87  			"oldName": oldName,
    88  		})
    89  		return nil
    90  	}
    91  
    92  	defer func() {
    93  		if retErr != nil {
    94  			container.Name = oldName
    95  			if err := container.CheckpointTo(daemon.containersReplica); err != nil {
    96  				log.G(context.TODO()).WithFields(log.Fields{
    97  					"containerID": container.ID,
    98  					"error":       err,
    99  				}).Error("failed to write container state to disk during rename")
   100  			}
   101  		}
   102  	}()
   103  
   104  	if sid := container.NetworkSettings.SandboxID; sid != "" && daemon.netController != nil {
   105  		sb, err := daemon.netController.SandboxByID(sid)
   106  		if err != nil {
   107  			return err
   108  		}
   109  		if err = sb.Rename(strings.TrimPrefix(container.Name, "/")); err != nil {
   110  			return err
   111  		}
   112  		defer func() {
   113  			if retErr != nil {
   114  				if err := sb.Rename(oldName); err != nil {
   115  					log.G(context.TODO()).WithFields(log.Fields{
   116  						"sandboxID": sid,
   117  						"oldName":   oldName,
   118  						"newName":   newName,
   119  						"error":     err,
   120  					}).Errorf("failed to revert sandbox rename")
   121  				}
   122  			}
   123  		}()
   124  
   125  		for nwName, epConfig := range container.NetworkSettings.Networks {
   126  			nw, err := daemon.FindNetwork(nwName)
   127  			if err != nil {
   128  				return err
   129  			}
   130  
   131  			ep := sb.GetEndpoint(epConfig.EndpointID)
   132  			if ep == nil {
   133  				return fmt.Errorf("no endpoint attached to network %s found", nw.Name())
   134  			}
   135  
   136  			oldDNSNames := make([]string, len(epConfig.DNSNames))
   137  			copy(oldDNSNames, epConfig.DNSNames)
   138  
   139  			epConfig.DNSNames = buildEndpointDNSNames(container, epConfig.Aliases)
   140  			if err := ep.UpdateDNSNames(epConfig.DNSNames); err != nil {
   141  				return err
   142  			}
   143  
   144  			defer func(ep *libnetwork.Endpoint, epConfig *network.EndpointSettings, oldDNSNames []string) {
   145  				if retErr == nil {
   146  					return
   147  				}
   148  
   149  				epConfig.DNSNames = oldDNSNames
   150  				if err := ep.UpdateDNSNames(epConfig.DNSNames); err != nil {
   151  					log.G(context.TODO()).WithFields(log.Fields{
   152  						"sandboxID": sid,
   153  						"oldName":   oldName,
   154  						"newName":   newName,
   155  						"error":     err,
   156  					}).Errorf("failed to revert DNSNames update")
   157  				}
   158  			}(ep, epConfig, oldDNSNames)
   159  		}
   160  	}
   161  
   162  	daemon.LogContainerEventWithAttributes(container, events.ActionRename, map[string]string{
   163  		"oldName": oldName,
   164  	})
   165  	return nil
   166  }