Browse code

Improve interface by moving to subpkg

Enable builds on OSX

Michael Crosby authored on 2013/12/19 09:42:49
Showing 13 changed files
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"fmt"
8 8
 	"github.com/dotcloud/docker/archive"
9 9
 	"github.com/dotcloud/docker/graphdriver"
10
+	"github.com/dotcloud/docker/mount"
10 11
 	"github.com/dotcloud/docker/term"
11 12
 	"github.com/dotcloud/docker/utils"
12 13
 	"github.com/kr/pty"
... ...
@@ -684,13 +685,41 @@ func (container *Container) Start() (err error) {
684 684
 		}
685 685
 	}
686 686
 
687
-	mounts, err := runtime.getMounts(container)
687
+	root := container.RootfsPath()
688
+	envPath, err := container.EnvConfigPath()
688 689
 	if err != nil {
689 690
 		return err
690 691
 	}
691 692
 
692
-	for _, m := range mounts {
693
-		if err := m.Mount(container.RootfsPath()); err != nil {
693
+	// Mount docker specific files into the containers root fs
694
+	if err := mount.Mount(runtime.sysInitPath, path.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil {
695
+		return err
696
+	}
697
+	if err := mount.Mount(envPath, path.Join(root, "/.dockerenv"), "none", "bind,ro"); err != nil {
698
+		return err
699
+	}
700
+	if err := mount.Mount(container.ResolvConfPath, path.Join(root, "/etc/resolv.conf"), "none", "bind,ro"); err != nil {
701
+		return err
702
+	}
703
+
704
+	if container.HostnamePath != "" && container.HostsPath != "" {
705
+		if err := mount.Mount(container.HostnamePath, path.Join(root, "/etc/hostname"), "none", "bind,ro"); err != nil {
706
+			return err
707
+		}
708
+		if err := mount.Mount(container.HostsPath, path.Join(root, "/etc/hosts"), "none", "bind,ro"); err != nil {
709
+			return err
710
+		}
711
+	}
712
+
713
+	// Mount user specified volumes
714
+
715
+	for r, v := range container.Volumes {
716
+		mountAs := "ro"
717
+		if container.VolumesRW[v] {
718
+			mountAs = "rw"
719
+		}
720
+
721
+		if err := mount.Mount(v, path.Join(root, r), "none", fmt.Sprintf("bind,%s", mountAs)); err != nil {
694 722
 			return err
695 723
 		}
696 724
 	}
... ...
@@ -1372,12 +1401,26 @@ func (container *Container) GetImage() (*Image, error) {
1372 1372
 }
1373 1373
 
1374 1374
 func (container *Container) Unmount() error {
1375
-	mounts, err := container.runtime.getMounts(container)
1376
-	if err != nil {
1377
-		return err
1375
+	var (
1376
+		err    error
1377
+		root   = container.RootfsPath()
1378
+		mounts = []string{
1379
+			path.Join(root, "/.dockerinit"),
1380
+			path.Join(root, "/.dockerenv"),
1381
+			path.Join(root, "/etc/resolv.conf"),
1382
+		}
1383
+	)
1384
+
1385
+	if container.HostnamePath != "" && container.HostsPath != "" {
1386
+		mounts = append(mounts, path.Join(root, "/etc/hostname"), path.Join(root, "/etc/hosts"))
1378 1387
 	}
1388
+
1389
+	for r := range container.Volumes {
1390
+		mounts = append(mounts, path.Join(root, r))
1391
+	}
1392
+
1379 1393
 	for _, m := range mounts {
1380
-		if lastError := m.Unmount(container.RootfsPath()); lastError != nil {
1394
+		if lastError := mount.Unmount(m); lastError != nil {
1381 1395
 			err = lastError
1382 1396
 		}
1383 1397
 	}
... ...
@@ -25,6 +25,7 @@ import (
25 25
 	"fmt"
26 26
 	"github.com/dotcloud/docker/archive"
27 27
 	"github.com/dotcloud/docker/graphdriver"
28
+	mountpk "github.com/dotcloud/docker/mount"
28 29
 	"github.com/dotcloud/docker/utils"
29 30
 	"os"
30 31
 	"os/exec"
... ...
@@ -295,7 +296,7 @@ func (a *Driver) unmount(id string) error {
295 295
 
296 296
 func (a *Driver) mounted(id string) (bool, error) {
297 297
 	target := path.Join(a.rootPath(), "mnt", id)
298
-	return graphdriver.Mounted(target)
298
+	return mountpk.Mounted(target)
299 299
 }
300 300
 
301 301
 // During cleanup aufs needs to unmount all mountpoints
... ...
@@ -1,19 +1,13 @@
1 1
 package graphdriver
2 2
 
3 3
 import (
4
-	"bufio"
5 4
 	"fmt"
6 5
 	"github.com/dotcloud/docker/archive"
7 6
 	"github.com/dotcloud/docker/utils"
8 7
 	"os"
9 8
 	"path"
10
-	"strings"
11
-	"syscall"
12
-	"time"
13 9
 )
14 10
 
15
-const mountinfoFormat = "%d %d %d:%d %s %s %s - %s %s %s"
16
-
17 11
 type InitFunc func(root string) (Driver, error)
18 12
 
19 13
 type Driver interface {
... ...
@@ -37,13 +31,6 @@ type Differ interface {
37 37
 	DiffSize(id string) (bytes int64, err error)
38 38
 }
39 39
 
40
-type Mount struct {
41
-	Device  string
42
-	Target  string
43
-	Type    string
44
-	Options string
45
-}
46
-
47 40
 var (
48 41
 	DefaultDriver string
49 42
 	// All registred drivers
... ...
@@ -101,137 +88,3 @@ func New(root string) (driver Driver, err error) {
101 101
 	}
102 102
 	return nil, err
103 103
 }
104
-
105
-func (m *Mount) Mount(root string) error {
106
-	target := path.Join(root, m.Target)
107
-	if mounted, err := Mounted(target); err != nil || mounted {
108
-		return err
109
-	}
110
-
111
-	flag, data := parseOptions(m.Options)
112
-	if err := syscall.Mount(m.Device, target, m.Type, uintptr(flag), data); err != nil {
113
-		return err
114
-	}
115
-	return nil
116
-}
117
-
118
-func parseOptions(options string) (int, string) {
119
-	var (
120
-		flag int
121
-		data []string
122
-	)
123
-
124
-	flags := map[string]struct {
125
-		clear bool
126
-		flag  int
127
-	}{
128
-		"defaults":      {false, 0},
129
-		"ro":            {false, syscall.MS_RDONLY},
130
-		"rw":            {true, syscall.MS_RDONLY},
131
-		"suid":          {true, syscall.MS_NOSUID},
132
-		"nosuid":        {false, syscall.MS_NOSUID},
133
-		"dev":           {true, syscall.MS_NODEV},
134
-		"nodev":         {false, syscall.MS_NODEV},
135
-		"exec":          {true, syscall.MS_NOEXEC},
136
-		"noexec":        {false, syscall.MS_NOEXEC},
137
-		"sync":          {false, syscall.MS_SYNCHRONOUS},
138
-		"async":         {true, syscall.MS_SYNCHRONOUS},
139
-		"dirsync":       {false, syscall.MS_DIRSYNC},
140
-		"remount":       {false, syscall.MS_REMOUNT},
141
-		"mand":          {false, syscall.MS_MANDLOCK},
142
-		"nomand":        {true, syscall.MS_MANDLOCK},
143
-		"atime":         {true, syscall.MS_NOATIME},
144
-		"noatime":       {false, syscall.MS_NOATIME},
145
-		"diratime":      {true, syscall.MS_NODIRATIME},
146
-		"nodiratime":    {false, syscall.MS_NODIRATIME},
147
-		"bind":          {false, syscall.MS_BIND},
148
-		"rbind":         {false, syscall.MS_BIND | syscall.MS_REC},
149
-		"relatime":      {false, syscall.MS_RELATIME},
150
-		"norelatime":    {true, syscall.MS_RELATIME},
151
-		"strictatime":   {false, syscall.MS_STRICTATIME},
152
-		"nostrictatime": {true, syscall.MS_STRICTATIME},
153
-	}
154
-
155
-	for _, o := range strings.Split(options, ",") {
156
-		// If the option does not exist in the flags table then it is a
157
-		// data value for a specific fs type
158
-		if f, exists := flags[o]; exists {
159
-			if f.clear {
160
-				flag &= ^f.flag
161
-			} else {
162
-				flag |= f.flag
163
-			}
164
-		} else {
165
-			data = append(data, o)
166
-		}
167
-	}
168
-	return flag, strings.Join(data, ",")
169
-}
170
-
171
-func (m *Mount) Unmount(root string) (err error) {
172
-	target := path.Join(root, m.Target)
173
-	if mounted, err := Mounted(target); err != nil || !mounted {
174
-		return err
175
-	}
176
-
177
-	// Simple retry logic for unmount
178
-	for i := 0; i < 10; i++ {
179
-		if err = syscall.Unmount(target, 0); err == nil {
180
-			return nil
181
-		}
182
-		utils.Debugf("[Unmount] %s", err)
183
-		time.Sleep(100 * time.Millisecond)
184
-	}
185
-	return
186
-}
187
-
188
-func Mounted(mountpoint string) (bool, error) {
189
-	entries, err := parseMountTable()
190
-	if err != nil {
191
-		return false, err
192
-	}
193
-
194
-	// Search the table for the mountpoint
195
-	for _, e := range entries {
196
-		if e.mountpoint == mountpoint {
197
-			return true, nil
198
-		}
199
-	}
200
-	return false, nil
201
-}
202
-
203
-// Represents one line from /proc/self/mountinfo
204
-type procEntry struct {
205
-	id, parent, major, minor           int
206
-	source, mountpoint, fstype, device string
207
-	vfsopts, opts                      string
208
-}
209
-
210
-// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
211
-//
212
-// 180 20 0:2851 / /var/lib/docker/aufs/mnt/a22632d4ed3cb2438246064408f9f07734cbd331f50c81f1ca3dcbd78541ce83 rw,relatime - aufs none rw,si=e9663ac1adbdb4f8
213
-func parseMountTable() ([]*procEntry, error) {
214
-	f, err := os.Open("/proc/self/mountinfo")
215
-	if err != nil {
216
-		return nil, err
217
-	}
218
-	defer f.Close()
219
-
220
-	s := bufio.NewScanner(f)
221
-	out := []*procEntry{}
222
-	for s.Scan() {
223
-		if err := s.Err(); err != nil {
224
-			return nil, err
225
-		}
226
-
227
-		p := &procEntry{}
228
-		if _, err := fmt.Sscanf(s.Text(), mountinfoFormat,
229
-			&p.id, &p.parent, &p.major, &p.minor,
230
-			&p.source, &p.mountpoint, &p.vfsopts, &p.fstype,
231
-			&p.device, &p.opts); err != nil {
232
-			return nil, err
233
-		}
234
-		out = append(out, p)
235
-	}
236
-	return out, nil
237
-}
238 104
deleted file mode 100644
... ...
@@ -1,74 +0,0 @@
1
-package graphdriver
2
-
3
-import (
4
-	"os"
5
-	"path"
6
-	"syscall"
7
-	"testing"
8
-)
9
-
10
-func TestMountOptionsParsing(t *testing.T) {
11
-	options := "bind,ro,size=10k"
12
-
13
-	flag, data := parseOptions(options)
14
-
15
-	if data != "size=10k" {
16
-		t.Fatalf("Expected size=10 got %s", data)
17
-	}
18
-
19
-	expectedFlag := syscall.MS_BIND | syscall.MS_RDONLY
20
-
21
-	if flag != expectedFlag {
22
-		t.Fatalf("Expected %d got %d", expectedFlag, flag)
23
-	}
24
-}
25
-
26
-func TestMounted(t *testing.T) {
27
-	tmp := path.Join(os.TempDir(), "graphdriver-tests")
28
-	if err := os.MkdirAll(tmp, 0777); err != nil {
29
-		t.Fatal(err)
30
-	}
31
-	defer os.RemoveAll(tmp)
32
-
33
-	var (
34
-		sourcePath = path.Join(tmp, "sourcefile.txt")
35
-		targetPath = path.Join(tmp, "targetfile.txt")
36
-	)
37
-
38
-	f, err := os.Create(sourcePath)
39
-	if err != nil {
40
-		t.Fatal(err)
41
-	}
42
-	f.WriteString("hello")
43
-	f.Close()
44
-
45
-	f, err = os.Create(targetPath)
46
-	if err != nil {
47
-		t.Fatal(err)
48
-	}
49
-	f.Close()
50
-
51
-	mount := &Mount{
52
-		Device:  sourcePath,
53
-		Target:  targetPath,
54
-		Type:    "none",
55
-		Options: "bind,ro",
56
-	}
57
-
58
-	if err := mount.Mount("/"); err != nil {
59
-		t.Fatal(err)
60
-	}
61
-	defer func() {
62
-		if err := mount.Unmount("/"); err != nil {
63
-			t.Fatal(err)
64
-		}
65
-	}()
66
-
67
-	mounted, err := Mounted(targetPath)
68
-	if err != nil {
69
-		t.Fatal(err)
70
-	}
71
-	if !mounted {
72
-		t.Fatalf("Expected %s to be mounted", targetPath)
73
-	}
74
-}
75 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
0 1
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+package mount
1
+
2
+func parseOptions(options string) (int, string) {
3
+	panic("Not implemented")
4
+}
0 5
new file mode 100644
... ...
@@ -0,0 +1,61 @@
0
+package mount
1
+
2
+import (
3
+	"strings"
4
+	"syscall"
5
+)
6
+
7
+// Parse fstab type mount options into mount() flags
8
+// and device specific data
9
+func parseOptions(options string) (int, string) {
10
+	var (
11
+		flag int
12
+		data []string
13
+	)
14
+
15
+	flags := map[string]struct {
16
+		clear bool
17
+		flag  int
18
+	}{
19
+		"defaults":      {false, 0},
20
+		"ro":            {false, syscall.MS_RDONLY},
21
+		"rw":            {true, syscall.MS_RDONLY},
22
+		"suid":          {true, syscall.MS_NOSUID},
23
+		"nosuid":        {false, syscall.MS_NOSUID},
24
+		"dev":           {true, syscall.MS_NODEV},
25
+		"nodev":         {false, syscall.MS_NODEV},
26
+		"exec":          {true, syscall.MS_NOEXEC},
27
+		"noexec":        {false, syscall.MS_NOEXEC},
28
+		"sync":          {false, syscall.MS_SYNCHRONOUS},
29
+		"async":         {true, syscall.MS_SYNCHRONOUS},
30
+		"dirsync":       {false, syscall.MS_DIRSYNC},
31
+		"remount":       {false, syscall.MS_REMOUNT},
32
+		"mand":          {false, syscall.MS_MANDLOCK},
33
+		"nomand":        {true, syscall.MS_MANDLOCK},
34
+		"atime":         {true, syscall.MS_NOATIME},
35
+		"noatime":       {false, syscall.MS_NOATIME},
36
+		"diratime":      {true, syscall.MS_NODIRATIME},
37
+		"nodiratime":    {false, syscall.MS_NODIRATIME},
38
+		"bind":          {false, syscall.MS_BIND},
39
+		"rbind":         {false, syscall.MS_BIND | syscall.MS_REC},
40
+		"relatime":      {false, syscall.MS_RELATIME},
41
+		"norelatime":    {true, syscall.MS_RELATIME},
42
+		"strictatime":   {false, syscall.MS_STRICTATIME},
43
+		"nostrictatime": {true, syscall.MS_STRICTATIME},
44
+	}
45
+
46
+	for _, o := range strings.Split(options, ",") {
47
+		// If the option does not exist in the flags table then it is a
48
+		// data value for a specific fs type
49
+		if f, exists := flags[o]; exists {
50
+			if f.clear {
51
+				flag &= ^f.flag
52
+			} else {
53
+				flag |= f.flag
54
+			}
55
+		} else {
56
+			data = append(data, o)
57
+		}
58
+	}
59
+	return flag, strings.Join(data, ",")
60
+}
0 61
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+package mount
1
+
2
+import (
3
+	"time"
4
+)
5
+
6
+// Looks at /proc/self/mountinfo to determine of the specified
7
+// mountpoint has been mounted
8
+func Mounted(mountpoint string) (bool, error) {
9
+	entries, err := parseMountTable()
10
+	if err != nil {
11
+		return false, err
12
+	}
13
+
14
+	// Search the table for the mountpoint
15
+	for _, e := range entries {
16
+		if e.mountpoint == mountpoint {
17
+			return true, nil
18
+		}
19
+	}
20
+	return false, nil
21
+}
22
+
23
+// Mount the specified options at the target path
24
+// Options must be specified as fstab style
25
+func Mount(device, target, mType, options string) error {
26
+	if mounted, err := Mounted(target); err != nil || mounted {
27
+		return err
28
+	}
29
+
30
+	flag, data := parseOptions(options)
31
+	if err := mount(device, target, mType, uintptr(flag), data); err != nil {
32
+		return err
33
+	}
34
+	return nil
35
+
36
+}
37
+
38
+// Unmount the target only if it is mounted
39
+func Unmount(target string) (err error) {
40
+	if mounted, err := Mounted(target); err != nil || !mounted {
41
+		return err
42
+	}
43
+
44
+	// Simple retry logic for unmount
45
+	for i := 0; i < 10; i++ {
46
+		if err = unmount(target, 0); err == nil {
47
+			return nil
48
+		}
49
+		time.Sleep(100 * time.Millisecond)
50
+	}
51
+	return
52
+}
0 53
new file mode 100644
... ...
@@ -0,0 +1,67 @@
0
+package mount
1
+
2
+import (
3
+	"os"
4
+	"path"
5
+	"syscall"
6
+	"testing"
7
+)
8
+
9
+func TestMountOptionsParsing(t *testing.T) {
10
+	options := "bind,ro,size=10k"
11
+
12
+	flag, data := parseOptions(options)
13
+
14
+	if data != "size=10k" {
15
+		t.Fatalf("Expected size=10 got %s", data)
16
+	}
17
+
18
+	expectedFlag := syscall.MS_BIND | syscall.MS_RDONLY
19
+
20
+	if flag != expectedFlag {
21
+		t.Fatalf("Expected %d got %d", expectedFlag, flag)
22
+	}
23
+}
24
+
25
+func TestMounted(t *testing.T) {
26
+	tmp := path.Join(os.TempDir(), "mount-tests")
27
+	if err := os.MkdirAll(tmp, 0777); err != nil {
28
+		t.Fatal(err)
29
+	}
30
+	defer os.RemoveAll(tmp)
31
+
32
+	var (
33
+		sourcePath = path.Join(tmp, "sourcefile.txt")
34
+		targetPath = path.Join(tmp, "targetfile.txt")
35
+	)
36
+
37
+	f, err := os.Create(sourcePath)
38
+	if err != nil {
39
+		t.Fatal(err)
40
+	}
41
+	f.WriteString("hello")
42
+	f.Close()
43
+
44
+	f, err = os.Create(targetPath)
45
+	if err != nil {
46
+		t.Fatal(err)
47
+	}
48
+	f.Close()
49
+
50
+	if err := Mount(sourcePath, targetPath, "none", "bind,ro"); err != nil {
51
+		t.Fatal(err)
52
+	}
53
+	defer func() {
54
+		if err := Unmount(targetPath); err != nil {
55
+			t.Fatal(err)
56
+		}
57
+	}()
58
+
59
+	mounted, err := Mounted(targetPath)
60
+	if err != nil {
61
+		t.Fatal(err)
62
+	}
63
+	if !mounted {
64
+		t.Fatalf("Expected %s to be mounted", targetPath)
65
+	}
66
+}
0 67
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+package mount
1
+
2
+func mount(device, target, mType string, flag uintptr, data string) error {
3
+	panic("Not implemented")
4
+}
5
+
6
+func unmount(target string, flag int) error {
7
+	panic("Not implemented")
8
+}
0 9
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+package mount
1
+
2
+import (
3
+	"syscall"
4
+)
5
+
6
+func mount(device, target, mType string, flag uintptr, data string) error {
7
+	return syscall.Mount(device, target, mType, flag, data)
8
+}
9
+
10
+func unmount(target string, flag int) error {
11
+	return syscall.Unmount(target, flag)
12
+}
0 13
new file mode 100644
... ...
@@ -0,0 +1,45 @@
0
+package mount
1
+
2
+import (
3
+	"bufio"
4
+	"fmt"
5
+	"os"
6
+)
7
+
8
+const (
9
+	mountinfoFormat = "%d %d %d:%d %s %s %s - %s %s %s"
10
+)
11
+
12
+// Represents one line from /proc/self/mountinfo
13
+type procEntry struct {
14
+	id, parent, major, minor           int
15
+	source, mountpoint, fstype, device string
16
+	vfsopts, opts                      string
17
+}
18
+
19
+// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
20
+func parseMountTable() ([]*procEntry, error) {
21
+	f, err := os.Open("/proc/self/mountinfo")
22
+	if err != nil {
23
+		return nil, err
24
+	}
25
+	defer f.Close()
26
+
27
+	s := bufio.NewScanner(f)
28
+	out := []*procEntry{}
29
+	for s.Scan() {
30
+		if err := s.Err(); err != nil {
31
+			return nil, err
32
+		}
33
+
34
+		p := &procEntry{}
35
+		if _, err := fmt.Sscanf(s.Text(), mountinfoFormat,
36
+			&p.id, &p.parent, &p.major, &p.minor,
37
+			&p.source, &p.mountpoint, &p.vfsopts, &p.fstype,
38
+			&p.device, &p.opts); err != nil {
39
+			return nil, err
40
+		}
41
+		out = append(out, p)
42
+	}
43
+	return out, nil
44
+}
... ...
@@ -788,67 +788,6 @@ func (runtime *Runtime) Close() error {
788 788
 	return nil
789 789
 }
790 790
 
791
-func (runtime *Runtime) getMounts(container *Container) ([]*graphdriver.Mount, error) {
792
-	// Generate additional bind mounts
793
-	envPath, err := container.EnvConfigPath()
794
-	if err != nil {
795
-		return nil, err
796
-	}
797
-	mounts := []*graphdriver.Mount{
798
-		{
799
-			Device:  runtime.sysInitPath,
800
-			Target:  "/.dockerinit",
801
-			Type:    "none",
802
-			Options: "bind,ro",
803
-		},
804
-		{
805
-			Device:  envPath,
806
-			Target:  "/.dockerenv",
807
-			Type:    "none",
808
-			Options: "bind,ro",
809
-		},
810
-		// In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
811
-		{
812
-			Device:  container.ResolvConfPath,
813
-			Target:  "/etc/resolv.conf",
814
-			Type:    "none",
815
-			Options: "bind,ro",
816
-		},
817
-	}
818
-
819
-	if container.HostnamePath != "" && container.HostsPath != "" {
820
-		mounts = append(mounts,
821
-			&graphdriver.Mount{
822
-				Device:  container.HostnamePath,
823
-				Target:  "/etc/hostname",
824
-				Type:    "none",
825
-				Options: "bind,ro",
826
-			},
827
-			&graphdriver.Mount{
828
-				Device:  container.HostsPath,
829
-				Target:  "/etc/hosts",
830
-				Type:    "none",
831
-				Options: "bind,ro",
832
-			})
833
-	}
834
-
835
-	for r, v := range container.Volumes {
836
-		mountAs := "ro"
837
-		if container.VolumesRW[v] {
838
-			mountAs = "rw"
839
-		}
840
-
841
-		mounts = append(mounts,
842
-			&graphdriver.Mount{
843
-				Device:  v,
844
-				Target:  r,
845
-				Type:    "none",
846
-				Options: fmt.Sprintf("bind,%s", mountAs),
847
-			})
848
-	}
849
-	return mounts, nil
850
-}
851
-
852 791
 func (runtime *Runtime) Mount(container *Container) error {
853 792
 	dir, err := runtime.driver.Get(container.ID)
854 793
 	if err != nil {