Browse code

Restore volume refs after daemon restart

Volume refs were not being restored on daemon restart.
This made it possible to remove a volume being used by other containers
after a daemon restart.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2014/10/09 02:13:32
Showing 4 changed files
... ...
@@ -400,9 +400,7 @@ func (daemon *Daemon) restore() error {
400 400
 	}
401 401
 
402 402
 	for _, c := range registeredContainers {
403
-		for _, mnt := range c.VolumeMounts() {
404
-			daemon.volumes.Add(mnt.volume)
405
-		}
403
+		c.registerVolumes()
406 404
 	}
407 405
 
408 406
 	if !debug {
... ...
@@ -92,6 +92,12 @@ func (container *Container) VolumePaths() map[string]struct{} {
92 92
 	return paths
93 93
 }
94 94
 
95
+func (container *Container) registerVolumes() {
96
+	for _, mnt := range container.VolumeMounts() {
97
+		mnt.volume.AddContainer(container.ID)
98
+	}
99
+}
100
+
95 101
 func (container *Container) derefVolumes() {
96 102
 	for path := range container.VolumePaths() {
97 103
 		vol := container.daemon.volumes.Get(path)
... ...
@@ -1,6 +1,8 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"encoding/json"
5
+	"os"
4 6
 	"strings"
5 7
 	"testing"
6 8
 )
... ...
@@ -48,3 +50,35 @@ func TestDaemonRestartWithRunningContainersPorts(t *testing.T) {
48 48
 
49 49
 	logDone("daemon - running containers on daemon restart")
50 50
 }
51
+
52
+func TestDaemonRestartWithVolumesRefs(t *testing.T) {
53
+	d := NewDaemon(t)
54
+	if err := d.StartWithBusybox(); err != nil {
55
+		t.Fatal(err)
56
+	}
57
+	defer d.Stop()
58
+
59
+	if out, err := d.Cmd("run", "-d", "--name", "volrestarttest1", "-v", "/foo", "busybox"); err != nil {
60
+		t.Fatal(err, out)
61
+	}
62
+	if err := d.Restart(); err != nil {
63
+		t.Fatal(err)
64
+	}
65
+	if _, err := d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox"); err != nil {
66
+		t.Fatal(err)
67
+	}
68
+	if out, err := d.Cmd("rm", "-fv", "volrestarttest2"); err != nil {
69
+		t.Fatal(err, out)
70
+	}
71
+	v, err := d.Cmd("inspect", "--format", "{{ json .Volumes }}", "volrestarttest1")
72
+	if err != nil {
73
+		t.Fatal(err)
74
+	}
75
+	volumes := make(map[string]string)
76
+	json.Unmarshal([]byte(v), &volumes)
77
+	if _, err := os.Stat(volumes["/foo"]); err != nil {
78
+		t.Fatalf("Expected volume to exist: %s - %s", volumes["/foo"], err)
79
+	}
80
+
81
+	logDone("daemon - volume refs are restored")
82
+}
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"sync"
9 9
 
10 10
 	"github.com/docker/docker/daemon/graphdriver"
11
+	"github.com/docker/docker/pkg/log"
11 12
 	"github.com/docker/docker/utils"
12 13
 )
13 14
 
... ...
@@ -83,11 +84,31 @@ func (r *Repository) restore() error {
83 83
 		return err
84 84
 	}
85 85
 
86
-	var ids []string
87 86
 	for _, v := range dir {
88 87
 		id := v.Name()
89
-		if r.driver.Exists(id) {
90
-			ids = append(ids, id)
88
+		path, err := r.driver.Get(id, "")
89
+		if err != nil {
90
+			log.Debugf("Could not find volume for %s: %v", id, err)
91
+			continue
92
+		}
93
+		vol := &Volume{
94
+			ID:         id,
95
+			configPath: r.configPath + "/" + id,
96
+			containers: make(map[string]struct{}),
97
+			Path:       path,
98
+		}
99
+		if err := vol.FromDisk(); err != nil {
100
+			if !os.IsNotExist(err) {
101
+				log.Debugf("Error restoring volume: %v", err)
102
+				continue
103
+			}
104
+			if err := vol.initialize(); err != nil {
105
+				log.Debugf("%s", err)
106
+				continue
107
+			}
108
+		}
109
+		if err := r.add(vol); err != nil {
110
+			log.Debugf("Error restoring volume: %v", err)
91 111
 		}
92 112
 	}
93 113
 	return nil
... ...
@@ -173,7 +194,7 @@ func (r *Repository) createNewVolumePath(id string) (string, error) {
173 173
 
174 174
 	path, err := r.driver.Get(id, "")
175 175
 	if err != nil {
176
-		return "", fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", r.driver, id, err)
176
+		return "", fmt.Errorf("Driver %s failed to get volume rootfs %s: %v", r.driver, id, err)
177 177
 	}
178 178
 
179 179
 	return path, nil