1package daemon // import "github.com/docker/docker/daemon"
2
3import (
4	"strings"
5
6	dockercontainer "github.com/docker/docker/container"
7	"github.com/docker/docker/errdefs"
8	"github.com/docker/libnetwork"
9	"github.com/pkg/errors"
10	"github.com/sirupsen/logrus"
11)
12
13// ContainerRename changes the name of a container, using the oldName
14// to find the container. An error is returned if newName is already
15// reserved.
16func (daemon *Daemon) ContainerRename(oldName, newName string) error {
17	var (
18		sid string
19		sb  libnetwork.Sandbox
20	)
21
22	if oldName == "" || newName == "" {
23		return errdefs.InvalidParameter(errors.New("Neither old nor new names may be empty"))
24	}
25
26	if newName[0] != '/' {
27		newName = "/" + newName
28	}
29
30	container, err := daemon.GetContainer(oldName)
31	if err != nil {
32		return err
33	}
34
35	container.Lock()
36	defer container.Unlock()
37
38	oldName = container.Name
39	oldIsAnonymousEndpoint := container.NetworkSettings.IsAnonymousEndpoint
40
41	if oldName == newName {
42		return errdefs.InvalidParameter(errors.New("Renaming a container with the same name as its current name"))
43	}
44
45	links := map[string]*dockercontainer.Container{}
46	for k, v := range daemon.linkIndex.children(container) {
47		if !strings.HasPrefix(k, oldName) {
48			return errdefs.InvalidParameter(errors.Errorf("Linked container %s does not match parent %s", k, oldName))
49		}
50		links[strings.TrimPrefix(k, oldName)] = v
51	}
52
53	if newName, err = daemon.reserveName(container.ID, newName); err != nil {
54		return errors.Wrap(err, "Error when allocating new name")
55	}
56
57	for k, v := range links {
58		daemon.containersReplica.ReserveName(newName+k, v.ID)
59		daemon.linkIndex.link(container, v, newName+k)
60	}
61
62	container.Name = newName
63	container.NetworkSettings.IsAnonymousEndpoint = false
64
65	defer func() {
66		if err != nil {
67			container.Name = oldName
68			container.NetworkSettings.IsAnonymousEndpoint = oldIsAnonymousEndpoint
69			daemon.reserveName(container.ID, oldName)
70			for k, v := range links {
71				daemon.containersReplica.ReserveName(oldName+k, v.ID)
72				daemon.linkIndex.link(container, v, oldName+k)
73				daemon.linkIndex.unlink(newName+k, v, container)
74				daemon.containersReplica.ReleaseName(newName + k)
75			}
76			daemon.releaseName(newName)
77		}
78	}()
79
80	for k, v := range links {
81		daemon.linkIndex.unlink(oldName+k, v, container)
82		daemon.containersReplica.ReleaseName(oldName + k)
83	}
84	daemon.releaseName(oldName)
85	if err = container.CheckpointTo(daemon.containersReplica); err != nil {
86		return err
87	}
88
89	attributes := map[string]string{
90		"oldName": oldName,
91	}
92
93	if !container.Running {
94		daemon.LogContainerEventWithAttributes(container, "rename", attributes)
95		return nil
96	}
97
98	defer func() {
99		if err != nil {
100			container.Name = oldName
101			container.NetworkSettings.IsAnonymousEndpoint = oldIsAnonymousEndpoint
102			if e := container.CheckpointTo(daemon.containersReplica); e != nil {
103				logrus.Errorf("%s: Failed in writing to Disk on rename failure: %v", container.ID, e)
104			}
105		}
106	}()
107
108	sid = container.NetworkSettings.SandboxID
109	if sid != "" && daemon.netController != nil {
110		sb, err = daemon.netController.SandboxByID(sid)
111		if err != nil {
112			return err
113		}
114
115		err = sb.Rename(strings.TrimPrefix(container.Name, "/"))
116		if err != nil {
117			return err
118		}
119	}
120
121	daemon.LogContainerEventWithAttributes(container, "rename", attributes)
122	return nil
123}
124