Browse code

Merge pull request #9705 from acbodine/9311-truncindex-error-duplicate-id-on-ambiguous-id

Closes #9311 Handles container id/name collisions against daemon functionalities according to #8069

Michael Crosby authored on 2015/02/07 07:01:28
Showing 32 changed files
... ...
@@ -863,6 +863,12 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
863 863
 	for _, name := range cmd.Args() {
864 864
 		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
865 865
 		if err != nil {
866
+			if strings.Contains(err.Error(), "Too many") {
867
+				fmt.Fprintf(cli.err, "Error: %s", err.Error())
868
+				status = 1
869
+				continue
870
+			}
871
+
866 872
 			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
867 873
 			if err != nil {
868 874
 				if strings.Contains(err.Error(), "No such") {
... ...
@@ -1114,7 +1114,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
1114 1114
 	w.Header().Set("Content-Type", "application/x-tar")
1115 1115
 	if err := job.Run(); err != nil {
1116 1116
 		log.Errorf("%s", err.Error())
1117
-		if strings.Contains(strings.ToLower(err.Error()), "no such container") {
1117
+		if strings.Contains(strings.ToLower(err.Error()), "no such id") {
1118 1118
 			w.WriteHeader(http.StatusNotFound)
1119 1119
 		} else if strings.Contains(err.Error(), "no such file or directory") {
1120 1120
 			return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])
... ...
@@ -87,9 +87,9 @@ func (b *Builder) commit(id string, autoCmd []string, comment string) error {
87 87
 		}
88 88
 		defer container.Unmount()
89 89
 	}
90
-	container := b.Daemon.Get(id)
91
-	if container == nil {
92
-		return fmt.Errorf("An error occured while creating the container")
90
+	container, err := b.Daemon.Get(id)
91
+	if err != nil {
92
+		return err
93 93
 	}
94 94
 
95 95
 	// Note: Actually copy the struct
... ...
@@ -710,7 +710,11 @@ func fixPermissions(source, destination string, uid, gid int, destExisted bool)
710 710
 
711 711
 func (b *Builder) clearTmp() {
712 712
 	for c := range b.TmpContainers {
713
-		tmp := b.Daemon.Get(c)
713
+		tmp, err := b.Daemon.Get(c)
714
+		if err != nil {
715
+			fmt.Fprint(b.OutStream, err.Error())
716
+		}
717
+
714 718
 		if err := b.Daemon.Destroy(tmp); err != nil {
715 719
 			fmt.Fprintf(b.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
716 720
 			return
... ...
@@ -28,9 +28,9 @@ func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status {
28 28
 		stderr = job.GetenvBool("stderr")
29 29
 	)
30 30
 
31
-	container := daemon.Get(name)
32
-	if container == nil {
33
-		return job.Errorf("No such container: %s", name)
31
+	container, err := daemon.Get(name)
32
+	if err != nil {
33
+		return job.Error(err)
34 34
 	}
35 35
 
36 36
 	//logs
... ...
@@ -9,24 +9,29 @@ func (daemon *Daemon) ContainerChanges(job *engine.Job) engine.Status {
9 9
 		return job.Errorf("Usage: %s CONTAINER", job.Name)
10 10
 	}
11 11
 	name := job.Args[0]
12
-	if container := daemon.Get(name); container != nil {
13
-		outs := engine.NewTable("", 0)
14
-		changes, err := container.Changes()
15
-		if err != nil {
16
-			return job.Error(err)
17
-		}
18
-		for _, change := range changes {
19
-			out := &engine.Env{}
20
-			if err := out.Import(change); err != nil {
21
-				return job.Error(err)
22
-			}
23
-			outs.Add(out)
24
-		}
25
-		if _, err := outs.WriteListTo(job.Stdout); err != nil {
12
+
13
+	container, error := daemon.Get(name)
14
+	if error != nil {
15
+		return job.Error(error)
16
+	}
17
+
18
+	outs := engine.NewTable("", 0)
19
+	changes, err := container.Changes()
20
+	if err != nil {
21
+		return job.Error(err)
22
+	}
23
+
24
+	for _, change := range changes {
25
+		out := &engine.Env{}
26
+		if err := out.Import(change); err != nil {
26 27
 			return job.Error(err)
27 28
 		}
28
-	} else {
29
-		return job.Errorf("No such container: %s", name)
29
+		outs.Add(out)
30 30
 	}
31
+
32
+	if _, err := outs.WriteListTo(job.Stdout); err != nil {
33
+		return job.Error(err)
34
+	}
35
+
31 36
 	return engine.StatusOK
32 37
 }
... ...
@@ -12,9 +12,9 @@ func (daemon *Daemon) ContainerCommit(job *engine.Job) engine.Status {
12 12
 	}
13 13
 	name := job.Args[0]
14 14
 
15
-	container := daemon.Get(name)
16
-	if container == nil {
17
-		return job.Errorf("No such container: %s", name)
15
+	container, err := daemon.Get(name)
16
+	if err != nil {
17
+		return job.Error(err)
18 18
 	}
19 19
 
20 20
 	var (
... ...
@@ -1126,7 +1126,12 @@ func (container *Container) updateParentsHosts() error {
1126 1126
 		if ref.ParentID == "0" {
1127 1127
 			continue
1128 1128
 		}
1129
-		c := container.daemon.Get(ref.ParentID)
1129
+
1130
+		c, err := container.daemon.Get(ref.ParentID)
1131
+		if err != nil {
1132
+			log.Error(err)
1133
+		}
1134
+
1130 1135
 		if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() {
1131 1136
 			log.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
1132 1137
 			if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, ref.Name); err != nil {
... ...
@@ -1395,9 +1400,9 @@ func (container *Container) GetMountLabel() string {
1395 1395
 
1396 1396
 func (container *Container) getIpcContainer() (*Container, error) {
1397 1397
 	containerID := container.hostConfig.IpcMode.Container()
1398
-	c := container.daemon.Get(containerID)
1399
-	if c == nil {
1400
-		return nil, fmt.Errorf("no such container to join IPC: %s", containerID)
1398
+	c, err := container.daemon.Get(containerID)
1399
+	if err != nil {
1400
+		return nil, err
1401 1401
 	}
1402 1402
 	if !c.IsRunning() {
1403 1403
 		return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID)
... ...
@@ -1412,9 +1417,9 @@ func (container *Container) getNetworkedContainer() (*Container, error) {
1412 1412
 		if len(parts) != 2 {
1413 1413
 			return nil, fmt.Errorf("no container specified to join network")
1414 1414
 		}
1415
-		nc := container.daemon.Get(parts[1])
1416
-		if nc == nil {
1417
-			return nil, fmt.Errorf("no such container to join network: %s", parts[1])
1415
+		nc, err := container.daemon.Get(parts[1])
1416
+		if err != nil {
1417
+			return nil, err
1418 1418
 		}
1419 1419
 		if !nc.IsRunning() {
1420 1420
 			return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1])
... ...
@@ -16,18 +16,19 @@ func (daemon *Daemon) ContainerCopy(job *engine.Job) engine.Status {
16 16
 		resource = job.Args[1]
17 17
 	)
18 18
 
19
-	if container := daemon.Get(name); container != nil {
19
+	container, err := daemon.Get(name)
20
+	if err != nil {
21
+		return job.Error(err)
22
+	}
20 23
 
21
-		data, err := container.Copy(resource)
22
-		if err != nil {
23
-			return job.Error(err)
24
-		}
25
-		defer data.Close()
24
+	data, err := container.Copy(resource)
25
+	if err != nil {
26
+		return job.Error(err)
27
+	}
28
+	defer data.Close()
26 29
 
27
-		if _, err := io.Copy(job.Stdout, data); err != nil {
28
-			return job.Error(err)
29
-		}
30
-		return engine.StatusOK
30
+	if _, err := io.Copy(job.Stdout, data); err != nil {
31
+		return job.Error(err)
31 32
 	}
32
-	return job.Errorf("No such container: %s", name)
33
+	return engine.StatusOK
33 34
 }
... ...
@@ -132,9 +132,9 @@ func (daemon *Daemon) GenerateSecurityOpt(ipcMode runconfig.IpcMode, pidMode run
132 132
 		return label.DisableSecOpt(), nil
133 133
 	}
134 134
 	if ipcContainer := ipcMode.Container(); ipcContainer != "" {
135
-		c := daemon.Get(ipcContainer)
136
-		if c == nil {
137
-			return nil, fmt.Errorf("no such container to join IPC: %s", ipcContainer)
135
+		c, err := daemon.Get(ipcContainer)
136
+		if err != nil {
137
+			return nil, err
138 138
 		}
139 139
 		if !c.IsRunning() {
140 140
 			return nil, fmt.Errorf("cannot join IPC of a non running container: %s", ipcContainer)
... ...
@@ -155,28 +155,39 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
155 155
 	return nil
156 156
 }
157 157
 
158
-// Get looks for a container by the specified ID or name, and returns it.
159
-// If the container is not found, or if an error occurs, nil is returned.
160
-func (daemon *Daemon) Get(name string) *Container {
161
-	id, err := daemon.idIndex.Get(name)
162
-	if err == nil {
163
-		return daemon.containers.Get(id)
158
+// Get looks for a container with the provided prefix
159
+func (daemon *Daemon) Get(prefix string) (*Container, error) {
160
+	if containerByID := daemon.containers.Get(prefix); containerByID != nil {
161
+
162
+		// prefix is an exact match to a full container ID
163
+		return containerByID, nil
164 164
 	}
165 165
 
166
-	if c, _ := daemon.GetByName(name); c != nil {
167
-		return c
166
+	// Either GetByName finds an entity matching prefix exactly, or it doesn't.
167
+	// Check value of containerByName and ignore any errors
168
+	containerByName, _ := daemon.GetByName(prefix)
169
+	containerId, indexError := daemon.idIndex.Get(prefix)
170
+
171
+	if containerByName != nil {
172
+
173
+		// prefix is an exact match to a full container Name
174
+		return containerByName, nil
168 175
 	}
169 176
 
170
-	if err == truncindex.ErrDuplicateID {
171
-		log.Errorf("Short ID %s is ambiguous: please retry with more characters or use the full ID.\n", name)
177
+	if containerId != "" {
178
+
179
+		// prefix is a fuzzy match to a container ID
180
+		return daemon.containers.Get(containerId), nil
172 181
 	}
173
-	return nil
182
+
183
+	return nil, indexError
174 184
 }
175 185
 
176 186
 // Exists returns a true if a container of the specified ID or name exists,
177 187
 // false otherwise.
178 188
 func (daemon *Daemon) Exists(id string) bool {
179
-	return daemon.Get(id) != nil
189
+	c, _ := daemon.Get(id)
190
+	return c != nil
180 191
 }
181 192
 
182 193
 func (daemon *Daemon) containerRoot(id string) string {
... ...
@@ -715,9 +726,9 @@ func (daemon *Daemon) Children(name string) (map[string]*Container, error) {
715 715
 	children := make(map[string]*Container)
716 716
 
717 717
 	err = daemon.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
718
-		c := daemon.Get(e.ID())
719
-		if c == nil {
720
-			return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
718
+		c, err := daemon.Get(e.ID())
719
+		if err != nil {
720
+			return err
721 721
 		}
722 722
 		children[p] = c
723 723
 		return nil
... ...
@@ -754,7 +765,10 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
754 754
 			if err != nil {
755 755
 				return err
756 756
 			}
757
-			child := daemon.Get(parts["name"])
757
+			child, err := daemon.Get(parts["name"])
758
+			if err != nil {
759
+				return err
760
+			}
758 761
 			if child == nil {
759 762
 				return fmt.Errorf("Could not get container for %s", parts["name"])
760 763
 			}
... ...
@@ -1100,18 +1114,18 @@ func (daemon *Daemon) Stats(c *Container) (*execdriver.ResourceStats, error) {
1100 1100
 }
1101 1101
 
1102 1102
 func (daemon *Daemon) SubscribeToContainerStats(name string) (chan interface{}, error) {
1103
-	c := daemon.Get(name)
1104
-	if c == nil {
1105
-		return nil, fmt.Errorf("no such container")
1103
+	c, err := daemon.Get(name)
1104
+	if err != nil {
1105
+		return nil, err
1106 1106
 	}
1107 1107
 	ch := daemon.statsCollector.collect(c)
1108 1108
 	return ch, nil
1109 1109
 }
1110 1110
 
1111 1111
 func (daemon *Daemon) UnsubscribeToContainerStats(name string, ch chan interface{}) error {
1112
-	c := daemon.Get(name)
1113
-	if c == nil {
1114
-		return fmt.Errorf("no such container")
1112
+	c, err := daemon.Get(name)
1113
+	if err != nil {
1114
+		return err
1115 1115
 	}
1116 1116
 	daemon.statsCollector.unsubscribe(c, ch)
1117 1117
 	return nil
1118 1118
new file mode 100644
... ...
@@ -0,0 +1,101 @@
0
+package daemon
1
+
2
+import (
3
+	"github.com/docker/docker/pkg/graphdb"
4
+	"github.com/docker/docker/pkg/truncindex"
5
+	"os"
6
+	"path"
7
+	"testing"
8
+)
9
+
10
+//
11
+// https://github.com/docker/docker/issues/8069
12
+//
13
+
14
+func TestGet(t *testing.T) {
15
+	c1 := &Container{
16
+		ID:   "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
17
+		Name: "tender_bardeen",
18
+	}
19
+	c2 := &Container{
20
+		ID:   "3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de",
21
+		Name: "drunk_hawking",
22
+	}
23
+	c3 := &Container{
24
+		ID:   "3cdbd1aa394fd68559fd1441d6eff2abfafdcba06e72d2febdba229008b0bf57",
25
+		Name: "3cdbd1aa",
26
+	}
27
+	c4 := &Container{
28
+		ID:   "75fb0b800922abdbef2d27e60abcdfaf7fb0698b2a96d22d3354da361a6ff4a5",
29
+		Name: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
30
+	}
31
+	c5 := &Container{
32
+		ID:   "d22d69a2b8960bf7fafdcba06e72d2febdba960bf7fafdcba06e72d2f9008b060b",
33
+		Name: "d22d69a2b896",
34
+	}
35
+
36
+	store := &contStore{
37
+		s: map[string]*Container{
38
+			c1.ID: c1,
39
+			c2.ID: c2,
40
+			c3.ID: c3,
41
+			c4.ID: c4,
42
+			c5.ID: c5,
43
+		},
44
+	}
45
+
46
+	index := truncindex.NewTruncIndex([]string{})
47
+	index.Add(c1.ID)
48
+	index.Add(c2.ID)
49
+	index.Add(c3.ID)
50
+	index.Add(c4.ID)
51
+	index.Add(c5.ID)
52
+
53
+	daemonTestDbPath := path.Join(os.TempDir(), "daemon_test.db")
54
+	graph, err := graphdb.NewSqliteConn(daemonTestDbPath)
55
+	if err != nil {
56
+		t.Fatalf("Failed to create daemon test sqlite database at %s", daemonTestDbPath)
57
+	}
58
+	graph.Set(c1.Name, c1.ID)
59
+	graph.Set(c2.Name, c2.ID)
60
+	graph.Set(c3.Name, c3.ID)
61
+	graph.Set(c4.Name, c4.ID)
62
+	graph.Set(c5.Name, c5.ID)
63
+
64
+	daemon := &Daemon{
65
+		containers:     store,
66
+		idIndex:        index,
67
+		containerGraph: graph,
68
+	}
69
+
70
+	if container, _ := daemon.Get("3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de"); container != c2 {
71
+		t.Fatal("Should explicitly match full container IDs")
72
+	}
73
+
74
+	if container, _ := daemon.Get("75fb0b8009"); container != c4 {
75
+		t.Fatal("Should match a partial ID")
76
+	}
77
+
78
+	if container, _ := daemon.Get("drunk_hawking"); container != c2 {
79
+		t.Fatal("Should match a full name")
80
+	}
81
+
82
+	// c3.Name is a partial match for both c3.ID and c2.ID
83
+	if c, _ := daemon.Get("3cdbd1aa"); c != c3 {
84
+		t.Fatal("Should match a full name even though it collides with another container's ID")
85
+	}
86
+
87
+	if container, _ := daemon.Get("d22d69a2b896"); container != c5 {
88
+		t.Fatal("Should match a container where the provided prefix is an exact match to the it's name, and is also a prefix for it's ID")
89
+	}
90
+
91
+	if _, err := daemon.Get("3cdbd1"); err == nil {
92
+		t.Fatal("Should return an error when provided a prefix that partially matches multiple container ID's")
93
+	}
94
+
95
+	if _, err := daemon.Get("nothing"); err == nil {
96
+		t.Fatal("Should return an error when provided a prefix that is neither a name or a partial match to an ID")
97
+	}
98
+
99
+	os.Remove(daemonTestDbPath)
100
+}
... ...
@@ -17,10 +17,10 @@ func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status {
17 17
 	removeVolume := job.GetenvBool("removeVolume")
18 18
 	removeLink := job.GetenvBool("removeLink")
19 19
 	forceRemove := job.GetenvBool("forceRemove")
20
-	container := daemon.Get(name)
21 20
 
22
-	if container == nil {
23
-		return job.Errorf("No such container: %s", name)
21
+	container, err := daemon.Get(name)
22
+	if err != nil {
23
+		return job.Error(err)
24 24
 	}
25 25
 
26 26
 	if removeLink {
... ...
@@ -36,7 +36,7 @@ func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status {
36 36
 		if pe == nil {
37 37
 			return job.Errorf("Cannot get parent %s for name %s", parent, name)
38 38
 		}
39
-		parentContainer := daemon.Get(pe.ID())
39
+		parentContainer, _ := daemon.Get(pe.ID())
40 40
 
41 41
 		if parentContainer != nil {
42 42
 			parentContainer.DisableLink(n)
... ...
@@ -97,10 +97,9 @@ func (d *Daemon) unregisterExecCommand(execConfig *execConfig) {
97 97
 }
98 98
 
99 99
 func (d *Daemon) getActiveContainer(name string) (*Container, error) {
100
-	container := d.Get(name)
101
-
102
-	if container == nil {
103
-		return nil, fmt.Errorf("No such container: %s", name)
100
+	container, err := d.Get(name)
101
+	if err != nil {
102
+		return nil, err
104 103
 	}
105 104
 
106 105
 	if !container.IsRunning() {
... ...
@@ -11,20 +11,23 @@ func (daemon *Daemon) ContainerExport(job *engine.Job) engine.Status {
11 11
 		return job.Errorf("Usage: %s container_id", job.Name)
12 12
 	}
13 13
 	name := job.Args[0]
14
-	if container := daemon.Get(name); container != nil {
15
-		data, err := container.Export()
16
-		if err != nil {
17
-			return job.Errorf("%s: %s", name, err)
18
-		}
19
-		defer data.Close()
20 14
 
21
-		// Stream the entire contents of the container (basically a volatile snapshot)
22
-		if _, err := io.Copy(job.Stdout, data); err != nil {
23
-			return job.Errorf("%s: %s", name, err)
24
-		}
25
-		// FIXME: factor job-specific LogEvent to engine.Job.Run()
26
-		container.LogEvent("export")
27
-		return engine.StatusOK
15
+	container, err := daemon.Get(name)
16
+	if err != nil {
17
+		return job.Error(err)
28 18
 	}
29
-	return job.Errorf("No such container: %s", name)
19
+
20
+	data, err := container.Export()
21
+	if err != nil {
22
+		return job.Errorf("%s: %s", name, err)
23
+	}
24
+	defer data.Close()
25
+
26
+	// Stream the entire contents of the container (basically a volatile snapshot)
27
+	if _, err := io.Copy(job.Stdout, data); err != nil {
28
+		return job.Errorf("%s: %s", name, err)
29
+	}
30
+	// FIXME: factor job-specific LogEvent to engine.Job.Run()
31
+	container.LogEvent("export")
32
+	return engine.StatusOK
30 33
 }
... ...
@@ -13,60 +13,62 @@ func (daemon *Daemon) ContainerInspect(job *engine.Job) engine.Status {
13 13
 		return job.Errorf("usage: %s NAME", job.Name)
14 14
 	}
15 15
 	name := job.Args[0]
16
-	if container := daemon.Get(name); container != nil {
17
-		container.Lock()
18
-		defer container.Unlock()
19
-		if job.GetenvBool("raw") {
20
-			b, err := json.Marshal(&struct {
21
-				*Container
22
-				HostConfig *runconfig.HostConfig
23
-			}{container, container.hostConfig})
24
-			if err != nil {
25
-				return job.Error(err)
26
-			}
27
-			job.Stdout.Write(b)
28
-			return engine.StatusOK
16
+	container, err := daemon.Get(name)
17
+	if err != nil {
18
+		return job.Error(err)
19
+	}
20
+
21
+	container.Lock()
22
+	defer container.Unlock()
23
+	if job.GetenvBool("raw") {
24
+		b, err := json.Marshal(&struct {
25
+			*Container
26
+			HostConfig *runconfig.HostConfig
27
+		}{container, container.hostConfig})
28
+		if err != nil {
29
+			return job.Error(err)
29 30
 		}
31
+		job.Stdout.Write(b)
32
+		return engine.StatusOK
33
+	}
30 34
 
31
-		out := &engine.Env{}
32
-		out.SetJson("Id", container.ID)
33
-		out.SetAuto("Created", container.Created)
34
-		out.SetJson("Path", container.Path)
35
-		out.SetList("Args", container.Args)
36
-		out.SetJson("Config", container.Config)
37
-		out.SetJson("State", container.State)
38
-		out.Set("Image", container.ImageID)
39
-		out.SetJson("NetworkSettings", container.NetworkSettings)
40
-		out.Set("ResolvConfPath", container.ResolvConfPath)
41
-		out.Set("HostnamePath", container.HostnamePath)
42
-		out.Set("HostsPath", container.HostsPath)
43
-		out.SetJson("Name", container.Name)
44
-		out.SetInt("RestartCount", container.RestartCount)
45
-		out.Set("Driver", container.Driver)
46
-		out.Set("ExecDriver", container.ExecDriver)
47
-		out.Set("MountLabel", container.MountLabel)
48
-		out.Set("ProcessLabel", container.ProcessLabel)
49
-		out.SetJson("Volumes", container.Volumes)
50
-		out.SetJson("VolumesRW", container.VolumesRW)
51
-		out.SetJson("AppArmorProfile", container.AppArmorProfile)
35
+	out := &engine.Env{}
36
+	out.SetJson("Id", container.ID)
37
+	out.SetAuto("Created", container.Created)
38
+	out.SetJson("Path", container.Path)
39
+	out.SetList("Args", container.Args)
40
+	out.SetJson("Config", container.Config)
41
+	out.SetJson("State", container.State)
42
+	out.Set("Image", container.ImageID)
43
+	out.SetJson("NetworkSettings", container.NetworkSettings)
44
+	out.Set("ResolvConfPath", container.ResolvConfPath)
45
+	out.Set("HostnamePath", container.HostnamePath)
46
+	out.Set("HostsPath", container.HostsPath)
47
+	out.SetJson("Name", container.Name)
48
+	out.SetInt("RestartCount", container.RestartCount)
49
+	out.Set("Driver", container.Driver)
50
+	out.Set("ExecDriver", container.ExecDriver)
51
+	out.Set("MountLabel", container.MountLabel)
52
+	out.Set("ProcessLabel", container.ProcessLabel)
53
+	out.SetJson("Volumes", container.Volumes)
54
+	out.SetJson("VolumesRW", container.VolumesRW)
55
+	out.SetJson("AppArmorProfile", container.AppArmorProfile)
52 56
 
53
-		out.SetList("ExecIDs", container.GetExecIDs())
57
+	out.SetList("ExecIDs", container.GetExecIDs())
54 58
 
55
-		if children, err := daemon.Children(container.Name); err == nil {
56
-			for linkAlias, child := range children {
57
-				container.hostConfig.Links = append(container.hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
58
-			}
59
+	if children, err := daemon.Children(container.Name); err == nil {
60
+		for linkAlias, child := range children {
61
+			container.hostConfig.Links = append(container.hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
59 62
 		}
63
+	}
60 64
 
61
-		out.SetJson("HostConfig", container.hostConfig)
65
+	out.SetJson("HostConfig", container.hostConfig)
62 66
 
63
-		container.hostConfig.Links = nil
64
-		if _, err := out.WriteTo(job.Stdout); err != nil {
65
-			return job.Error(err)
66
-		}
67
-		return engine.StatusOK
67
+	container.hostConfig.Links = nil
68
+	if _, err := out.WriteTo(job.Stdout); err != nil {
69
+		return job.Error(err)
68 70
 	}
69
-	return job.Errorf("No such container: %s", name)
71
+	return engine.StatusOK
70 72
 }
71 73
 
72 74
 func (daemon *Daemon) ContainerExecInspect(job *engine.Job) engine.Status {
... ...
@@ -38,22 +38,23 @@ func (daemon *Daemon) ContainerKill(job *engine.Job) engine.Status {
38 38
 		}
39 39
 	}
40 40
 
41
-	if container := daemon.Get(name); container != nil {
42
-		// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
43
-		if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
44
-			if err := container.Kill(); err != nil {
45
-				return job.Errorf("Cannot kill container %s: %s", name, err)
46
-			}
47
-			container.LogEvent("kill")
48
-		} else {
49
-			// Otherwise, just send the requested signal
50
-			if err := container.KillSig(int(sig)); err != nil {
51
-				return job.Errorf("Cannot kill container %s: %s", name, err)
52
-			}
53
-			// FIXME: Add event for signals
41
+	container, err := daemon.Get(name)
42
+	if err != nil {
43
+		return job.Error(err)
44
+	}
45
+
46
+	// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
47
+	if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
48
+		if err := container.Kill(); err != nil {
49
+			return job.Errorf("Cannot kill container %s: %s", name, err)
54 50
 		}
51
+		container.LogEvent("kill")
55 52
 	} else {
56
-		return job.Errorf("No such container: %s", name)
53
+		// Otherwise, just send the requested signal
54
+		if err := container.KillSig(int(sig)); err != nil {
55
+			return job.Errorf("Cannot kill container %s: %s", name, err)
56
+		}
57
+		// FIXME: Add event for signals
57 58
 	}
58 59
 	return engine.StatusOK
59 60
 }
... ...
@@ -63,16 +63,16 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
63 63
 
64 64
 	var beforeCont, sinceCont *Container
65 65
 	if before != "" {
66
-		beforeCont = daemon.Get(before)
67
-		if beforeCont == nil {
68
-			return job.Error(fmt.Errorf("Could not find container with name or id %s", before))
66
+		beforeCont, err = daemon.Get(before)
67
+		if err != nil {
68
+			return job.Error(err)
69 69
 		}
70 70
 	}
71 71
 
72 72
 	if since != "" {
73
-		sinceCont = daemon.Get(since)
74
-		if sinceCont == nil {
75
-			return job.Error(fmt.Errorf("Could not find container with name or id %s", since))
73
+		sinceCont, err = daemon.Get(since)
74
+		if err != nil {
75
+			return job.Error(err)
76 76
 		}
77 77
 	}
78 78
 
... ...
@@ -40,9 +40,9 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status {
40 40
 	if tail == "" {
41 41
 		tail = "all"
42 42
 	}
43
-	container := daemon.Get(name)
44
-	if container == nil {
45
-		return job.Errorf("No such container: %s", name)
43
+	container, err := daemon.Get(name)
44
+	if err != nil {
45
+		return job.Error(err)
46 46
 	}
47 47
 	cLog, err := container.ReadLog("json")
48 48
 	if err != nil && os.IsNotExist(err) {
... ...
@@ -9,9 +9,9 @@ func (daemon *Daemon) ContainerPause(job *engine.Job) engine.Status {
9 9
 		return job.Errorf("Usage: %s CONTAINER", job.Name)
10 10
 	}
11 11
 	name := job.Args[0]
12
-	container := daemon.Get(name)
13
-	if container == nil {
14
-		return job.Errorf("No such container: %s", name)
12
+	container, err := daemon.Get(name)
13
+	if err != nil {
14
+		return job.Error(err)
15 15
 	}
16 16
 	if err := container.Pause(); err != nil {
17 17
 		return job.Errorf("Cannot pause container %s: %s", name, err)
... ...
@@ -25,9 +25,9 @@ func (daemon *Daemon) ContainerUnpause(job *engine.Job) engine.Status {
25 25
 		return job.Errorf("Usage: %s CONTAINER", job.Name)
26 26
 	}
27 27
 	name := job.Args[0]
28
-	container := daemon.Get(name)
29
-	if container == nil {
30
-		return job.Errorf("No such container: %s", name)
28
+	container, err := daemon.Get(name)
29
+	if err != nil {
30
+		return job.Error(err)
31 31
 	}
32 32
 	if err := container.Unpause(); err != nil {
33 33
 		return job.Errorf("Cannot unpause container %s: %s", name, err)
... ...
@@ -11,9 +11,9 @@ func (daemon *Daemon) ContainerRename(job *engine.Job) engine.Status {
11 11
 	oldName := job.Args[0]
12 12
 	newName := job.Args[1]
13 13
 
14
-	container := daemon.Get(oldName)
15
-	if container == nil {
16
-		return job.Errorf("No such container: %s", oldName)
14
+	container, err := daemon.Get(oldName)
15
+	if err != nil {
16
+		return job.Error(err)
17 17
 	}
18 18
 
19 19
 	oldName = container.Name
... ...
@@ -19,14 +19,14 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status {
19 19
 	if err != nil {
20 20
 		return job.Error(err)
21 21
 	}
22
-
23
-	if container := daemon.Get(name); container != nil {
24
-		if err := container.Resize(height, width); err != nil {
25
-			return job.Error(err)
26
-		}
27
-		return engine.StatusOK
22
+	container, err := daemon.Get(name)
23
+	if err != nil {
24
+		return job.Error(err)
28 25
 	}
29
-	return job.Errorf("No such container: %s", name)
26
+	if err := container.Resize(height, width); err != nil {
27
+		return job.Error(err)
28
+	}
29
+	return engine.StatusOK
30 30
 }
31 31
 
32 32
 func (daemon *Daemon) ContainerExecResize(job *engine.Job) engine.Status {
... ...
@@ -15,13 +15,13 @@ func (daemon *Daemon) ContainerRestart(job *engine.Job) engine.Status {
15 15
 	if job.EnvExists("t") {
16 16
 		t = job.GetenvInt("t")
17 17
 	}
18
-	if container := daemon.Get(name); container != nil {
19
-		if err := container.Restart(int(t)); err != nil {
20
-			return job.Errorf("Cannot restart container %s: %s\n", name, err)
21
-		}
22
-		container.LogEvent("restart")
23
-	} else {
24
-		return job.Errorf("No such container: %s\n", name)
18
+	container, err := daemon.Get(name)
19
+	if err != nil {
20
+		return job.Error(err)
25 21
 	}
22
+	if err := container.Restart(int(t)); err != nil {
23
+		return job.Errorf("Cannot restart container %s: %s\n", name, err)
24
+	}
25
+	container.LogEvent("restart")
26 26
 	return engine.StatusOK
27 27
 }
... ...
@@ -14,12 +14,12 @@ func (daemon *Daemon) ContainerStart(job *engine.Job) engine.Status {
14 14
 		return job.Errorf("Usage: %s container_id", job.Name)
15 15
 	}
16 16
 	var (
17
-		name      = job.Args[0]
18
-		container = daemon.Get(name)
17
+		name = job.Args[0]
19 18
 	)
20 19
 
21
-	if container == nil {
22
-		return job.Errorf("No such container: %s", name)
20
+	container, err := daemon.Get(name)
21
+	if err != nil {
22
+		return job.Error(err)
23 23
 	}
24 24
 
25 25
 	if container.IsPaused() {
... ...
@@ -15,16 +15,16 @@ func (daemon *Daemon) ContainerStop(job *engine.Job) engine.Status {
15 15
 	if job.EnvExists("t") {
16 16
 		t = job.GetenvInt("t")
17 17
 	}
18
-	if container := daemon.Get(name); container != nil {
19
-		if !container.IsRunning() {
20
-			return job.Errorf("Container already stopped")
21
-		}
22
-		if err := container.Stop(int(t)); err != nil {
23
-			return job.Errorf("Cannot stop container %s: %s\n", name, err)
24
-		}
25
-		container.LogEvent("stop")
26
-	} else {
27
-		return job.Errorf("No such container: %s\n", name)
18
+	container, err := daemon.Get(name)
19
+	if err != nil {
20
+		return job.Error(err)
28 21
 	}
22
+	if !container.IsRunning() {
23
+		return job.Errorf("Container already stopped")
24
+	}
25
+	if err := container.Stop(int(t)); err != nil {
26
+		return job.Errorf("Cannot stop container %s: %s\n", name, err)
27
+	}
28
+	container.LogEvent("stop")
29 29
 	return engine.StatusOK
30 30
 }
... ...
@@ -21,59 +21,59 @@ func (daemon *Daemon) ContainerTop(job *engine.Job) engine.Status {
21 21
 		psArgs = job.Args[1]
22 22
 	}
23 23
 
24
-	if container := daemon.Get(name); container != nil {
25
-		if !container.IsRunning() {
26
-			return job.Errorf("Container %s is not running", name)
27
-		}
28
-		pids, err := daemon.ExecutionDriver().GetPidsForContainer(container.ID)
29
-		if err != nil {
30
-			return job.Error(err)
31
-		}
32
-		output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output()
33
-		if err != nil {
34
-			return job.Errorf("Error running ps: %s", err)
35
-		}
24
+	container, err := daemon.Get(name)
25
+	if err != nil {
26
+		return job.Error(err)
27
+	}
28
+	if !container.IsRunning() {
29
+		return job.Errorf("Container %s is not running", name)
30
+	}
31
+	pids, err := daemon.ExecutionDriver().GetPidsForContainer(container.ID)
32
+	if err != nil {
33
+		return job.Error(err)
34
+	}
35
+	output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output()
36
+	if err != nil {
37
+		return job.Errorf("Error running ps: %s", err)
38
+	}
36 39
 
37
-		lines := strings.Split(string(output), "\n")
38
-		header := strings.Fields(lines[0])
39
-		out := &engine.Env{}
40
-		out.SetList("Titles", header)
40
+	lines := strings.Split(string(output), "\n")
41
+	header := strings.Fields(lines[0])
42
+	out := &engine.Env{}
43
+	out.SetList("Titles", header)
41 44
 
42
-		pidIndex := -1
43
-		for i, name := range header {
44
-			if name == "PID" {
45
-				pidIndex = i
46
-			}
47
-		}
48
-		if pidIndex == -1 {
49
-			return job.Errorf("Couldn't find PID field in ps output")
45
+	pidIndex := -1
46
+	for i, name := range header {
47
+		if name == "PID" {
48
+			pidIndex = i
50 49
 		}
50
+	}
51
+	if pidIndex == -1 {
52
+		return job.Errorf("Couldn't find PID field in ps output")
53
+	}
51 54
 
52
-		processes := [][]string{}
53
-		for _, line := range lines[1:] {
54
-			if len(line) == 0 {
55
-				continue
56
-			}
57
-			fields := strings.Fields(line)
58
-			p, err := strconv.Atoi(fields[pidIndex])
59
-			if err != nil {
60
-				return job.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
61
-			}
55
+	processes := [][]string{}
56
+	for _, line := range lines[1:] {
57
+		if len(line) == 0 {
58
+			continue
59
+		}
60
+		fields := strings.Fields(line)
61
+		p, err := strconv.Atoi(fields[pidIndex])
62
+		if err != nil {
63
+			return job.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
64
+		}
62 65
 
63
-			for _, pid := range pids {
64
-				if pid == p {
65
-					// Make sure number of fields equals number of header titles
66
-					// merging "overhanging" fields
67
-					process := fields[:len(header)-1]
68
-					process = append(process, strings.Join(fields[len(header)-1:], " "))
69
-					processes = append(processes, process)
70
-				}
66
+		for _, pid := range pids {
67
+			if pid == p {
68
+				// Make sure number of fields equals number of header titles
69
+				// merging "overhanging" fields
70
+				process := fields[:len(header)-1]
71
+				process = append(process, strings.Join(fields[len(header)-1:], " "))
72
+				processes = append(processes, process)
71 73
 			}
72 74
 		}
73
-		out.SetJson("Processes", processes)
74
-		out.WriteTo(job.Stdout)
75
-		return engine.StatusOK
76
-
77 75
 	}
78
-	return job.Errorf("No such container: %s", name)
76
+	out.SetJson("Processes", processes)
77
+	out.WriteTo(job.Stdout)
78
+	return engine.StatusOK
79 79
 }
... ...
@@ -266,9 +266,9 @@ func (container *Container) applyVolumesFrom() error {
266 266
 			continue
267 267
 		}
268 268
 
269
-		c := container.daemon.Get(id)
270
-		if c == nil {
271
-			return fmt.Errorf("container %s not found, impossible to mount its volumes", id)
269
+		c, err := container.daemon.Get(id)
270
+		if err != nil {
271
+			return err
272 272
 		}
273 273
 
274 274
 		var (
... ...
@@ -11,10 +11,11 @@ func (daemon *Daemon) ContainerWait(job *engine.Job) engine.Status {
11 11
 		return job.Errorf("Usage: %s", job.Name)
12 12
 	}
13 13
 	name := job.Args[0]
14
-	if container := daemon.Get(name); container != nil {
15
-		status, _ := container.WaitStop(-1 * time.Second)
16
-		job.Printf("%d\n", status)
17
-		return engine.StatusOK
14
+	container, err := daemon.Get(name)
15
+	if err != nil {
16
+		return job.Errorf("%s: %s", job.Name, err.Error())
18 17
 	}
19
-	return job.Errorf("%s: No such container: %s", job.Name, name)
18
+	status, _ := container.WaitStop(-1 * time.Second)
19
+	job.Printf("%d\n", status)
20
+	return engine.StatusOK
20 21
 }
... ...
@@ -325,7 +325,7 @@ func TestPostCreateNull(t *testing.T) {
325 325
 
326 326
 	containerAssertExists(eng, containerID, t)
327 327
 
328
-	c := daemon.Get(containerID)
328
+	c, _ := daemon.Get(containerID)
329 329
 	if c.Config.Cpuset != "" {
330 330
 		t.Fatalf("Cpuset should have been empty - instead its:" + c.Config.Cpuset)
331 331
 	}
... ...
@@ -282,12 +282,12 @@ func TestDaemonCreate(t *testing.T) {
282 282
 	}
283 283
 
284 284
 	// Make sure we can get the container with Get()
285
-	if daemon.Get(container.ID) == nil {
285
+	if _, err := daemon.Get(container.ID); err != nil {
286 286
 		t.Errorf("Unable to get newly created container")
287 287
 	}
288 288
 
289 289
 	// Make sure it is the right container
290
-	if daemon.Get(container.ID) != container {
290
+	if c, _ := daemon.Get(container.ID); c != container {
291 291
 		t.Errorf("Get() returned the wrong container")
292 292
 	}
293 293
 
... ...
@@ -383,8 +383,8 @@ func TestDestroy(t *testing.T) {
383 383
 	}
384 384
 
385 385
 	// Make sure daemon.Get() refuses to return the unexisting container
386
-	if daemon.Get(container.ID) != nil {
387
-		t.Errorf("Unable to get newly created container")
386
+	if c, _ := daemon.Get(container.ID); c != nil {
387
+		t.Errorf("Got a container that should not exist")
388 388
 	}
389 389
 
390 390
 	// Test double destroy
... ...
@@ -407,16 +407,16 @@ func TestGet(t *testing.T) {
407 407
 	container3, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t)
408 408
 	defer daemon.Destroy(container3)
409 409
 
410
-	if daemon.Get(container1.ID) != container1 {
411
-		t.Errorf("Get(test1) returned %v while expecting %v", daemon.Get(container1.ID), container1)
410
+	if c, _ := daemon.Get(container1.ID); c != container1 {
411
+		t.Errorf("Get(test1) returned %v while expecting %v", c, container1)
412 412
 	}
413 413
 
414
-	if daemon.Get(container2.ID) != container2 {
415
-		t.Errorf("Get(test2) returned %v while expecting %v", daemon.Get(container2.ID), container2)
414
+	if c, _ := daemon.Get(container2.ID); c != container2 {
415
+		t.Errorf("Get(test2) returned %v while expecting %v", c, container2)
416 416
 	}
417 417
 
418
-	if daemon.Get(container3.ID) != container3 {
419
-		t.Errorf("Get(test3) returned %v while expecting %v", daemon.Get(container3.ID), container3)
418
+	if c, _ := daemon.Get(container3.ID); c != container3 {
419
+		t.Errorf("Get(test3) returned %v while expecting %v", c, container3)
420 420
 	}
421 421
 
422 422
 }
... ...
@@ -485,9 +485,9 @@ func startEchoServerContainer(t *testing.T, proto string) (*daemon.Daemon, *daem
485 485
 		t.Fatal(err)
486 486
 	}
487 487
 
488
-	container := daemon.Get(id)
489
-	if container == nil {
490
-		t.Fatalf("Couldn't fetch test container %s", id)
488
+	container, err := daemon.Get(id)
489
+	if err != nil {
490
+		t.Fatal(err)
491 491
 	}
492 492
 
493 493
 	setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() {
... ...
@@ -646,8 +646,8 @@ func TestRestore(t *testing.T) {
646 646
 	if runningCount != 0 {
647 647
 		t.Fatalf("Expected 0 container alive, %d found", runningCount)
648 648
 	}
649
-	container3 := daemon2.Get(container1.ID)
650
-	if container3 == nil {
649
+	container3, err := daemon2.Get(container1.ID)
650
+	if err != nil {
651 651
 		t.Fatal("Unable to Get container")
652 652
 	}
653 653
 	if err := container3.Run(); err != nil {
... ...
@@ -666,16 +666,21 @@ func TestDefaultContainerName(t *testing.T) {
666 666
 		t.Fatal(err)
667 667
 	}
668 668
 
669
-	container := daemon.Get(createNamedTestContainer(eng, config, t, "some_name"))
669
+	container, err := daemon.Get(createNamedTestContainer(eng, config, t, "some_name"))
670
+	if err != nil {
671
+		t.Fatal(err)
672
+	}
670 673
 	containerID := container.ID
671 674
 
672 675
 	if container.Name != "/some_name" {
673 676
 		t.Fatalf("Expect /some_name got %s", container.Name)
674 677
 	}
675 678
 
676
-	if c := daemon.Get("/some_name"); c == nil {
679
+	c, err := daemon.Get("/some_name")
680
+	if err != nil {
677 681
 		t.Fatalf("Couldn't retrieve test container as /some_name")
678
-	} else if c.ID != containerID {
682
+	}
683
+	if c.ID != containerID {
679 684
 		t.Fatalf("Container /some_name has ID %s instead of %s", c.ID, containerID)
680 685
 	}
681 686
 }
... ...
@@ -690,14 +695,17 @@ func TestRandomContainerName(t *testing.T) {
690 690
 		t.Fatal(err)
691 691
 	}
692 692
 
693
-	container := daemon.Get(createTestContainer(eng, config, t))
693
+	container, err := daemon.Get(createTestContainer(eng, config, t))
694
+	if err != nil {
695
+		t.Fatal(err)
696
+	}
694 697
 	containerID := container.ID
695 698
 
696 699
 	if container.Name == "" {
697 700
 		t.Fatalf("Expected not empty container name")
698 701
 	}
699 702
 
700
-	if c := daemon.Get(container.Name); c == nil {
703
+	if c, err := daemon.Get(container.Name); err != nil {
701 704
 		log.Fatalf("Could not lookup container %s by its name", container.Name)
702 705
 	} else if c.ID != containerID {
703 706
 		log.Fatalf("Looking up container name %s returned id %s instead of %s", container.Name, c.ID, containerID)
... ...
@@ -737,13 +745,16 @@ func TestContainerNameValidation(t *testing.T) {
737 737
 			t.Fatal(err)
738 738
 		}
739 739
 
740
-		container := daemon.Get(engine.Tail(outputBuffer, 1))
740
+		container, err := daemon.Get(engine.Tail(outputBuffer, 1))
741
+		if err != nil {
742
+			t.Fatal(err)
743
+		}
741 744
 
742 745
 		if container.Name != "/"+test.Name {
743 746
 			t.Fatalf("Expect /%s got %s", test.Name, container.Name)
744 747
 		}
745 748
 
746
-		if c := daemon.Get("/" + test.Name); c == nil {
749
+		if c, err := daemon.Get("/" + test.Name); err != nil {
747 750
 			t.Fatalf("Couldn't retrieve test container as /%s", test.Name)
748 751
 		} else if c.ID != container.ID {
749 752
 			t.Fatalf("Container /%s has ID %s instead of %s", test.Name, c.ID, container.ID)
... ...
@@ -762,7 +773,10 @@ func TestLinkChildContainer(t *testing.T) {
762 762
 		t.Fatal(err)
763 763
 	}
764 764
 
765
-	container := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
765
+	container, err := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
766
+	if err != nil {
767
+		t.Fatal(err)
768
+	}
766 769
 
767 770
 	webapp, err := daemon.GetByName("/webapp")
768 771
 	if err != nil {
... ...
@@ -778,7 +792,10 @@ func TestLinkChildContainer(t *testing.T) {
778 778
 		t.Fatal(err)
779 779
 	}
780 780
 
781
-	childContainer := daemon.Get(createTestContainer(eng, config, t))
781
+	childContainer, err := daemon.Get(createTestContainer(eng, config, t))
782
+	if err != nil {
783
+		t.Fatal(err)
784
+	}
782 785
 
783 786
 	if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil {
784 787
 		t.Fatal(err)
... ...
@@ -804,7 +821,10 @@ func TestGetAllChildren(t *testing.T) {
804 804
 		t.Fatal(err)
805 805
 	}
806 806
 
807
-	container := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
807
+	container, err := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
808
+	if err != nil {
809
+		t.Fatal(err)
810
+	}
808 811
 
809 812
 	webapp, err := daemon.GetByName("/webapp")
810 813
 	if err != nil {
... ...
@@ -820,7 +840,10 @@ func TestGetAllChildren(t *testing.T) {
820 820
 		t.Fatal(err)
821 821
 	}
822 822
 
823
-	childContainer := daemon.Get(createTestContainer(eng, config, t))
823
+	childContainer, err := daemon.Get(createTestContainer(eng, config, t))
824
+	if err != nil {
825
+		t.Fatal(err)
826
+	}
824 827
 
825 828
 	if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil {
826 829
 		t.Fatal(err)
... ...
@@ -117,7 +117,7 @@ func containerAssertExists(eng *engine.Engine, id string, t Fataler) {
117 117
 
118 118
 func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) {
119 119
 	daemon := mkDaemonFromEngine(eng, t)
120
-	if c := daemon.Get(id); c != nil {
120
+	if c, _ := daemon.Get(id); c != nil {
121 121
 		t.Fatal(fmt.Errorf("Container %s should not exist", id))
122 122
 	}
123 123
 }
... ...
@@ -142,9 +142,9 @@ func assertHttpError(r *httptest.ResponseRecorder, t Fataler) {
142 142
 
143 143
 func getContainer(eng *engine.Engine, id string, t Fataler) *daemon.Container {
144 144
 	daemon := mkDaemonFromEngine(eng, t)
145
-	c := daemon.Get(id)
146
-	if c == nil {
147
-		t.Fatal(fmt.Errorf("No such container: %s", id))
145
+	c, err := daemon.Get(id)
146
+	if err != nil {
147
+		t.Fatal(err)
148 148
 	}
149 149
 	return c
150 150
 }
... ...
@@ -10,10 +10,8 @@ import (
10 10
 )
11 11
 
12 12
 var (
13
-	// ErrNoID is thrown when attempting to use empty prefixes
14
-	ErrNoID = errors.New("prefix can't be empty")
15
-	// ErrDuplicateID is thrown when a duplicated id was found
16
-	ErrDuplicateID = errors.New("multiple IDs were found")
13
+	ErrEmptyPrefix     = errors.New("Prefix can't be empty")
14
+	ErrAmbiguousPrefix = errors.New("Multiple IDs found with provided prefix")
17 15
 )
18 16
 
19 17
 func init() {
... ...
@@ -47,7 +45,7 @@ func (idx *TruncIndex) addID(id string) error {
47 47
 		return fmt.Errorf("illegal character: ' '")
48 48
 	}
49 49
 	if id == "" {
50
-		return ErrNoID
50
+		return ErrEmptyPrefix
51 51
 	}
52 52
 	if _, exists := idx.ids[id]; exists {
53 53
 		return fmt.Errorf("id already exists: '%s'", id)
... ...
@@ -87,26 +85,26 @@ func (idx *TruncIndex) Delete(id string) error {
87 87
 // Get retrieves an ID from the TruncIndex. If there are multiple IDs
88 88
 // with the given prefix, an error is thrown.
89 89
 func (idx *TruncIndex) Get(s string) (string, error) {
90
-	idx.RLock()
91
-	defer idx.RUnlock()
90
+	if s == "" {
91
+		return "", ErrEmptyPrefix
92
+	}
92 93
 	var (
93 94
 		id string
94 95
 	)
95
-	if s == "" {
96
-		return "", ErrNoID
97
-	}
98 96
 	subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error {
99 97
 		if id != "" {
100 98
 			// we haven't found the ID if there are two or more IDs
101 99
 			id = ""
102
-			return ErrDuplicateID
100
+			return ErrAmbiguousPrefix
103 101
 		}
104 102
 		id = string(prefix)
105 103
 		return nil
106 104
 	}
107 105
 
106
+	idx.RLock()
107
+	defer idx.RUnlock()
108 108
 	if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil {
109
-		return "", fmt.Errorf("no such id: %s", s)
109
+		return "", err
110 110
 	}
111 111
 	if id != "" {
112 112
 		return id, nil
... ...
@@ -59,6 +59,11 @@ func TestTruncIndex(t *testing.T) {
59 59
 	assertIndexGet(t, index, id[:4], "", true)
60 60
 	assertIndexGet(t, index, id[:1], "", true)
61 61
 
62
+	// An ambiguous id prefix should return an error
63
+	if _, err := index.Get(id[:4]); err == nil || err == nil {
64
+		t.Fatal("An ambiguous id prefix should return an error")
65
+	}
66
+
62 67
 	// 7 characters should NOT conflict
63 68
 	assertIndexGet(t, index, id[:7], id, false)
64 69
 	assertIndexGet(t, index, id2[:7], id2, false)