Browse code

Merge branch 'master' into docker-osx

Guillaume J. Charmes authored on 2013/11/29 14:16:32
Showing 21 changed files
... ...
@@ -4,8 +4,8 @@
4 4
 
5 5
 #### Notable features since 0.6.0
6 6
 
7
-* Storage drivers: choose from aufs, device mapper, vfs or btrfs.
8
-* Standard Linux support: docker now runs on unmodified linux kernels and all major distributions.
7
+* Storage drivers: choose from aufs, device-mapper, or vfs.
8
+* Standard Linux support: docker now runs on unmodified Linux kernels and all major distributions.
9 9
 * Links: compose complex software stacks by connecting containers to each other.
10 10
 * Container naming: organize your containers by giving them memorable names.
11 11
 * Advanced port redirects: specify port redirects per interface, or keep sensitive ports private.
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"mime"
16 16
 	"net"
17 17
 	"net/http"
18
+	"net/http/pprof"
18 19
 	"os"
19 20
 	"os/exec"
20 21
 	"regexp"
... ...
@@ -1037,9 +1038,21 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s
1037 1037
 	}
1038 1038
 }
1039 1039
 
1040
+func AttachProfiler(router *mux.Router) {
1041
+	router.HandleFunc("/debug/pprof/", pprof.Index)
1042
+	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
1043
+	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
1044
+	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
1045
+	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
1046
+	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
1047
+	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
1048
+}
1049
+
1040 1050
 func createRouter(srv *Server, logging bool) (*mux.Router, error) {
1041 1051
 	r := mux.NewRouter()
1042
-
1052
+	if os.Getenv("DEBUG") != "" {
1053
+		AttachProfiler(r)
1054
+	}
1043 1055
 	m := map[string]map[string]HttpApiFunc{
1044 1056
 		"GET": {
1045 1057
 			"/events":                         getEvents,
... ...
@@ -288,7 +288,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
288 288
 		destPath = destPath + "/"
289 289
 	}
290 290
 	if !strings.HasPrefix(origPath, b.context) {
291
-		return fmt.Errorf("Forbidden path: %s", origPath)
291
+		return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath)
292 292
 	}
293 293
 	fi, err := os.Stat(origPath)
294 294
 	if err != nil {
... ...
@@ -1666,8 +1666,11 @@ func (opts PathOpts) String() string { return fmt.Sprintf("%v", map[string]struc
1666 1666
 func (opts PathOpts) Set(val string) error {
1667 1667
 	var containerPath string
1668 1668
 
1669
-	splited := strings.SplitN(val, ":", 2)
1670
-	if len(splited) == 1 {
1669
+	if strings.Count(val, ":") > 2 {
1670
+		return fmt.Errorf("bad format for volumes: %s", val)
1671
+	}
1672
+
1673
+	if splited := strings.SplitN(val, ":", 2); len(splited) == 1 {
1671 1674
 		containerPath = splited[0]
1672 1675
 		val = filepath.Clean(splited[0])
1673 1676
 	} else {
... ...
@@ -1680,6 +1683,7 @@ func (opts PathOpts) Set(val string) error {
1680 1680
 		return fmt.Errorf("%s is not an absolute path", containerPath)
1681 1681
 	}
1682 1682
 	opts[val] = struct{}{}
1683
+
1683 1684
 	return nil
1684 1685
 }
1685 1686
 
... ...
@@ -2195,7 +2199,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
2195 2195
 }
2196 2196
 
2197 2197
 func (cli *DockerCli) CmdSave(args ...string) error {
2198
-	cmd := cli.Subcmd("save", "IMAGE DESTINATION", "Save an image to a tar archive")
2198
+	cmd := cli.Subcmd("save", "IMAGE", "Save an image to a tar archive (streamed to stdout)")
2199 2199
 	if err := cmd.Parse(args); err != nil {
2200 2200
 		return err
2201 2201
 	}
2202 2202
new file mode 100644
... ...
@@ -0,0 +1,157 @@
0
+package docker
1
+
2
+import (
3
+	"strings"
4
+	"testing"
5
+)
6
+
7
+func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
8
+	config, hostConfig, _, err := ParseRun(strings.Split(args+" ubuntu bash", " "), nil)
9
+	return config, hostConfig, err
10
+}
11
+
12
+func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
13
+	config, hostConfig, err := parse(t, args)
14
+	if err != nil {
15
+		t.Fatal(err)
16
+	}
17
+	return config, hostConfig
18
+}
19
+
20
+func TestParseRunLinks(t *testing.T) {
21
+	if _, hostConfig := mustParse(t, "-link a:b"); len(hostConfig.Links) == 0 || hostConfig.Links[0] != "a:b" {
22
+		t.Fatalf("Error parsing links. Expected []string{\"a:b\"}, received: %v", hostConfig.Links)
23
+	}
24
+	if _, hostConfig := mustParse(t, "-link a:b -link c:d"); len(hostConfig.Links) < 2 || hostConfig.Links[0] != "a:b" || hostConfig.Links[1] != "c:d" {
25
+		t.Fatalf("Error parsing links. Expected []string{\"a:b\", \"c:d\"}, received: %v", hostConfig.Links)
26
+	}
27
+	if _, hostConfig := mustParse(t, ""); len(hostConfig.Links) != 0 {
28
+		t.Fatalf("Error parsing links. No link expected, received: %v", hostConfig.Links)
29
+	}
30
+
31
+	if _, _, err := parse(t, "-link a"); err == nil {
32
+		t.Fatalf("Error parsing links. `-link a` should be an error but is not")
33
+	}
34
+	if _, _, err := parse(t, "-link"); err == nil {
35
+		t.Fatalf("Error parsing links. `-link` should be an error but is not")
36
+	}
37
+}
38
+
39
+func TestParseRunAttach(t *testing.T) {
40
+	if config, _ := mustParse(t, "-a stdin"); !config.AttachStdin || config.AttachStdout || config.AttachStderr {
41
+		t.Fatalf("Error parsing attach flags. Expect only Stdin enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
42
+	}
43
+	if config, _ := mustParse(t, "-a stdin -a stdout"); !config.AttachStdin || !config.AttachStdout || config.AttachStderr {
44
+		t.Fatalf("Error parsing attach flags. Expect only Stdin and Stdout enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
45
+	}
46
+	if config, _ := mustParse(t, "-a stdin -a stdout -a stderr"); !config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
47
+		t.Fatalf("Error parsing attach flags. Expect all attach enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
48
+	}
49
+	if config, _ := mustParse(t, ""); config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
50
+		t.Fatalf("Error parsing attach flags. Expect Stdin disabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
51
+	}
52
+
53
+	if _, _, err := parse(t, "-a"); err == nil {
54
+		t.Fatalf("Error parsing attach flags, `-a` should be an error but is not")
55
+	}
56
+	if _, _, err := parse(t, "-a invalid"); err == nil {
57
+		t.Fatalf("Error parsing attach flags, `-a invalid` should be an error but is not")
58
+	}
59
+	if _, _, err := parse(t, "-a invalid -a stdout"); err == nil {
60
+		t.Fatalf("Error parsing attach flags, `-a stdout -a invalid` should be an error but is not")
61
+	}
62
+	if _, _, err := parse(t, "-a stdout -a stderr -d"); err == nil {
63
+		t.Fatalf("Error parsing attach flags, `-a stdout -a stderr -d` should be an error but is not")
64
+	}
65
+	if _, _, err := parse(t, "-a stdin -d"); err == nil {
66
+		t.Fatalf("Error parsing attach flags, `-a stdin -d` should be an error but is not")
67
+	}
68
+	if _, _, err := parse(t, "-a stdout -d"); err == nil {
69
+		t.Fatalf("Error parsing attach flags, `-a stdout -d` should be an error but is not")
70
+	}
71
+	if _, _, err := parse(t, "-a stderr -d"); err == nil {
72
+		t.Fatalf("Error parsing attach flags, `-a stderr -d` should be an error but is not")
73
+	}
74
+	if _, _, err := parse(t, "-d -rm"); err == nil {
75
+		t.Fatalf("Error parsing attach flags, `-d -rm` should be an error but is not")
76
+	}
77
+}
78
+
79
+func TestParseRunVolumes(t *testing.T) {
80
+	if config, hostConfig := mustParse(t, "-v /tmp"); hostConfig.Binds != nil {
81
+		t.Fatalf("Error parsing volume flags, `-v /tmp` should not mount-bind anything. Received %v", hostConfig.Binds)
82
+	} else if _, exists := config.Volumes["/tmp"]; !exists {
83
+		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
84
+	}
85
+
86
+	if config, hostConfig := mustParse(t, "-v /tmp -v /var"); hostConfig.Binds != nil {
87
+		t.Fatalf("Error parsing volume flags, `-v /tmp -v /var` should not mount-bind anything. Received %v", hostConfig.Binds)
88
+	} else if _, exists := config.Volumes["/tmp"]; !exists {
89
+		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Recevied %v", config.Volumes)
90
+	} else if _, exists := config.Volumes["/var"]; !exists {
91
+		t.Fatalf("Error parsing volume flags, `-v /var` is missing from volumes. Received %v", config.Volumes)
92
+	}
93
+
94
+	if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
95
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp` should mount-bind /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
96
+	} else if _, exists := config.Volumes["/containerTmp"]; !exists {
97
+		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
98
+	}
99
+
100
+	if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /hostVar:/containerVar"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" || hostConfig.Binds[1] != "/hostVar:/containerVar" {
101
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /hostVar:/containerVar` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
102
+	} else if _, exists := config.Volumes["/containerTmp"]; !exists {
103
+		t.Fatalf("Error parsing volume flags, `-v /containerTmp` is missing from volumes. Received %v", config.Volumes)
104
+	} else if _, exists := config.Volumes["/containerVar"]; !exists {
105
+		t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
106
+	}
107
+
108
+	if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp:ro" || hostConfig.Binds[1] != "/hostVar:/containerVar:rw" {
109
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
110
+	} else if _, exists := config.Volumes["/containerTmp"]; !exists {
111
+		t.Fatalf("Error parsing volume flags, `-v /containerTmp` is missing from volumes. Received %v", config.Volumes)
112
+	} else if _, exists := config.Volumes["/containerVar"]; !exists {
113
+		t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
114
+	}
115
+
116
+	if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /containerVar"); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
117
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /containerVar` should mount-bind only /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
118
+	} else if _, exists := config.Volumes["/containerTmp"]; !exists {
119
+		t.Fatalf("Error parsing volume flags, `-v /containerTmp` is missing from volumes. Received %v", config.Volumes)
120
+	} else if _, exists := config.Volumes["/containerVar"]; !exists {
121
+		t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
122
+	}
123
+
124
+	if config, hostConfig := mustParse(t, ""); hostConfig.Binds != nil {
125
+		t.Fatalf("Error parsing volume flags, without volume, nothing should be mount-binded. Received %v", hostConfig.Binds)
126
+	} else if len(config.Volumes) != 0 {
127
+		t.Fatalf("Error parsing volume flags, without volume, no volume should be present. Received %v", config.Volumes)
128
+	}
129
+
130
+	mustParse(t, "-v /")
131
+
132
+	if _, _, err := parse(t, "-v /:/"); err == nil {
133
+		t.Fatalf("Error parsing volume flags, `-v /:/` should fail but didn't")
134
+	}
135
+	if _, _, err := parse(t, "-v"); err == nil {
136
+		t.Fatalf("Error parsing volume flags, `-v` should fail but didn't")
137
+	}
138
+	if _, _, err := parse(t, "-v /tmp:"); err == nil {
139
+		t.Fatalf("Error parsing volume flags, `-v /tmp:` should fail but didn't")
140
+	}
141
+	if _, _, err := parse(t, "-v /tmp:ro"); err == nil {
142
+		t.Fatalf("Error parsing volume flags, `-v /tmp:ro` should fail but didn't")
143
+	}
144
+	if _, _, err := parse(t, "-v /tmp::"); err == nil {
145
+		t.Fatalf("Error parsing volume flags, `-v /tmp::` should fail but didn't")
146
+	}
147
+	if _, _, err := parse(t, "-v :"); err == nil {
148
+		t.Fatalf("Error parsing volume flags, `-v :` should fail but didn't")
149
+	}
150
+	if _, _, err := parse(t, "-v ::"); err == nil {
151
+		t.Fatalf("Error parsing volume flags, `-v ::` should fail but didn't")
152
+	}
153
+	if _, _, err := parse(t, "-v /tmp:/tmp:/tmp:/tmp"); err == nil {
154
+		t.Fatalf("Error parsing volume flags, `-v /tmp:/tmp:/tmp:/tmp` should fail but didn't")
155
+	}
156
+}
... ...
@@ -17,3 +17,34 @@ meaning you can use Vagrant to control Docker containers.
17 17
 
18 18
 * [docker-provider](https://github.com/fgrehm/docker-provider)
19 19
 * [vagrant-shell](https://github.com/destructuring/vagrant-shell)
20
+
21
+## Setting up Vagrant-docker with the Remote API
22
+
23
+The initial Docker upstart script will not work because it runs on `127.0.0.1`, which is not accessible to the host machine. Instead, we need to change the script to connect to `0.0.0.0`. To do this, modify `/etc/init/docker.conf` to look like this:
24
+
25
+```
26
+description     "Docker daemon"
27
+
28
+start on filesystem and started lxc-net
29
+stop on runlevel [!2345]
30
+
31
+respawn
32
+
33
+script
34
+    /usr/bin/docker -d -H=tcp://0.0.0.0:4243/
35
+end script
36
+```
37
+
38
+Once that's done, you need to set up a SSH tunnel between your host machine and the vagrant machine that's running Docker. This can be done by running the following command in a host terminal:
39
+
40
+```
41
+ssh -L 4243:localhost:4243 -p 2222 vagrant@localhost
42
+```
43
+
44
+(The first 4243 is what your host can connect to, the second 4243 is what port Docker is running on in the vagrant machine, and the 2222 is the port Vagrant is providing for SSH. If VirtualBox is the VM you're using, you can see what value "2222" should be by going to: Network > Adapter 1 > Advanced > Port Forwarding in the VirtualBox GUI.)
45
+
46
+Note that because the port has been changed, to run docker commands from within the command line you must run them like this:
47
+
48
+```
49
+sudo docker -H 0.0.0.0:4243 < commands for docker >
50
+```
... ...
@@ -797,7 +797,7 @@ Known Issues (kill)
797 797
         -link="": Remove the link instead of the actual container
798 798
 
799 799
 Known Issues (rm)
800
-~~~~~~~~~~~~~~~~~~~
800
+~~~~~~~~~~~~~~~~~
801 801
 
802 802
 * :issue:`197` indicates that ``docker kill`` may leave directories
803 803
   behind and make it difficult to remove the container.
... ...
@@ -881,8 +881,15 @@ containers will not be deleted.
881 881
       -name="": Assign the specified name to the container. If no name is specific docker will generate a random name
882 882
       -P=false: Publish all exposed ports to the host interfaces
883 883
 
884
-Examples
884
+Known Issues (run -volumes-from)
885
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
886
+
887
+* :issue:`2702`: "lxc-start: Permission denied - failed to mount"
888
+  could indicate a permissions problem with AppArmor. Please see the
889
+  issue for a workaround.
890
+
891
+Examples:
892
+~~~~~~~~~
885 893
 
886 894
 .. code-block:: bash
887 895
 
... ...
@@ -974,16 +981,10 @@ id may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in
974 974
 read-only or read-write mode, respectively. By default, the volumes are mounted
975 975
 in the same mode (rw or ro) as the reference container.
976 976
 
977
-Known Issues (run -volumes-from)
978
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
979
-
980
-* :issue:`2702`: "lxc-start: Permission denied - failed to mount"
981
-  could indicate a permissions problem with AppArmor. Please see the
982
-  issue for a workaround.
983
-
984 977
 .. _cli_save:
985 978
 
986 979
 ``save``
980
+---------
987 981
 
988 982
 ::
989 983
 
... ...
@@ -131,8 +131,6 @@ Attach to the container to see the results in real-time.
131 131
 
132 132
 - **"docker attach**" This will allow us to attach to a background
133 133
   process to see what is going on.
134
-- **"-sig-proxy=true"** Proxify all received signal to the process
135
-  (even in non-tty mode)
136 134
 - **$CONTAINER_ID** The Id of the container we want to attach too.
137 135
 
138 136
 Exit from the container attachment by pressing Control-C.
... ...
@@ -45,19 +45,21 @@ Install ``python-software-properties``.
45 45
     apt-get -y install python-software-properties
46 46
     apt-get -y install software-properties-common
47 47
 
48
-Add Pitti's PostgreSQL repository. It contains the most recent stable release
49
-of PostgreSQL i.e. ``9.2``.
48
+Add PostgreSQL's repository. It contains the most recent stable release
49
+of PostgreSQL i.e. ``9.3``.
50 50
 
51 51
 .. code-block:: bash
52 52
 
53
-    add-apt-repository ppa:pitti/postgresql
53
+    apt-get -y install wget
54
+    wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
55
+    echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list
54 56
     apt-get update
55 57
 
56
-Finally, install PostgreSQL 9.2
58
+Finally, install PostgreSQL 9.3
57 59
 
58 60
 .. code-block:: bash
59 61
 
60
-    apt-get -y install postgresql-9.2 postgresql-client-9.2 postgresql-contrib-9.2
62
+    apt-get -y install postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3
61 63
 
62 64
 Now, create a PostgreSQL superuser role that can create databases and
63 65
 other roles.  Following Vagrant's convention the role will be named
... ...
@@ -76,14 +78,14 @@ role.
76 76
 
77 77
 Adjust PostgreSQL configuration so that remote connections to the
78 78
 database are possible. Make sure that inside
79
-``/etc/postgresql/9.2/main/pg_hba.conf`` you have following line (you will need
79
+``/etc/postgresql/9.3/main/pg_hba.conf`` you have following line (you will need
80 80
 to install an editor, e.g. ``apt-get install vim``):
81 81
 
82 82
 .. code-block:: bash
83 83
 
84 84
     host    all             all             0.0.0.0/0               md5
85 85
 
86
-Additionaly, inside ``/etc/postgresql/9.2/main/postgresql.conf``
86
+Additionaly, inside ``/etc/postgresql/9.3/main/postgresql.conf``
87 87
 uncomment ``listen_addresses`` so it is as follows:
88 88
 
89 89
 .. code-block:: bash
... ...
@@ -115,9 +117,9 @@ Finally, run PostgreSQL server via ``docker``.
115 115
 
116 116
     CONTAINER=$(sudo docker run -d -p 5432 \
117 117
       -t <your username>/postgresql \
118
-      /bin/su postgres -c '/usr/lib/postgresql/9.2/bin/postgres \
119
-        -D /var/lib/postgresql/9.2/main \
120
-        -c config_file=/etc/postgresql/9.2/main/postgresql.conf')
118
+      /bin/su postgres -c '/usr/lib/postgresql/9.3/bin/postgres \
119
+        -D /var/lib/postgresql/9.3/main \
120
+        -c config_file=/etc/postgresql/9.3/main/postgresql.conf')
121 121
 
122 122
 Connect the PostgreSQL server using ``psql`` (You will need postgres installed
123 123
 on the machine.  For ubuntu, use something like
... ...
@@ -132,7 +134,7 @@ As before, create roles or databases if needed.
132 132
 
133 133
 .. code-block:: bash
134 134
 
135
-    psql (9.2.4)
135
+    psql (9.3.1)
136 136
     Type "help" for help.
137 137
 
138 138
     docker=# CREATE DATABASE foo OWNER=docker;
... ...
@@ -160,9 +162,9 @@ container starts.
160 160
 .. code-block:: bash
161 161
 
162 162
     sudo docker commit -run='{"Cmd": \
163
-      ["/bin/su", "postgres", "-c", "/usr/lib/postgresql/9.2/bin/postgres -D \
164
-      /var/lib/postgresql/9.2/main -c \
165
-      config_file=/etc/postgresql/9.2/main/postgresql.conf"], "PortSpecs": ["5432"]}' \
163
+      ["/bin/su", "postgres", "-c", "/usr/lib/postgresql/9.3/bin/postgres -D \
164
+      /var/lib/postgresql/9.3/main -c \
165
+      config_file=/etc/postgresql/9.3/main/postgresql.conf"], "PortSpecs": ["5432"]}' \
166 166
       <container_id> <your username>/postgresql
167 167
 
168 168
 From now on, just type ``docker run <your username>/postgresql`` and
... ...
@@ -12,27 +12,28 @@ Arch Linux
12 12
 .. include:: install_unofficial.inc
13 13
 
14 14
 Installing on Arch Linux is not officially supported but can be handled via 
15
-either of the following AUR packages:
15
+one of the following AUR packages:
16 16
 
17 17
 * `lxc-docker <https://aur.archlinux.org/packages/lxc-docker/>`_
18 18
 * `lxc-docker-git <https://aur.archlinux.org/packages/lxc-docker-git/>`_
19
+* `lxc-docker-nightly <https://aur.archlinux.org/packages/lxc-docker-nightly/>`_
19 20
 
20 21
 The lxc-docker package will install the latest tagged version of docker. 
21 22
 The lxc-docker-git package will build from the current master branch.
23
+The lxc-docker-nightly package will install the latest build.
22 24
 
23 25
 Dependencies
24 26
 ------------
25 27
 
26 28
 Docker depends on several packages which are specified as dependencies in
27
-either AUR package.
29
+the AUR packages. The core dependencies are:
28 30
 
29
-* aufs3
30 31
 * bridge-utils
31
-* go
32
+* device-mapper
32 33
 * iproute2
33
-* linux-aufs_friendly
34 34
 * lxc
35 35
 
36
+
36 37
 Installation
37 38
 ------------
38 39
 
... ...
@@ -41,20 +42,14 @@ The instructions here assume **yaourt** is installed.  See
41 41
 for information on building and installing packages from the AUR if you have not
42 42
 done so before.
43 43
 
44
-Keep in mind that if **linux-aufs_friendly** is not already installed that a
45
-new kernel will be compiled and this can take quite a while.
46
-
47 44
 ::
48 45
 
49
-    yaourt -S lxc-docker-git
46
+    yaourt -S lxc-docker
50 47
 
51 48
 
52 49
 Starting Docker
53 50
 ---------------
54 51
 
55
-Prior to starting docker modify your bootloader to use the 
56
-**linux-aufs_friendly** kernel and reboot your system.
57
-
58 52
 There is a systemd service unit created for docker.  To start the docker service:
59 53
 
60 54
 ::
61 55
deleted file mode 100644
... ...
@@ -1,126 +0,0 @@
1
-// +build linux
2
-
3
-package devmapper
4
-
5
-import (
6
-	"fmt"
7
-	"github.com/dotcloud/docker/utils"
8
-)
9
-
10
-func stringToLoopName(src string) [LoNameSize]uint8 {
11
-	var dst [LoNameSize]uint8
12
-	copy(dst[:], src[:])
13
-	return dst
14
-}
15
-
16
-func getNextFreeLoopbackIndex() (int, error) {
17
-	f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644)
18
-	if err != nil {
19
-		return 0, err
20
-	}
21
-	defer f.Close()
22
-
23
-	index, err := ioctlLoopCtlGetFree(f.Fd())
24
-	if index < 0 {
25
-		index = 0
26
-	}
27
-	return index, err
28
-}
29
-
30
-func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, err error) {
31
-	// Start looking for a free /dev/loop
32
-	for {
33
-		target := fmt.Sprintf("/dev/loop%d", index)
34
-		index++
35
-
36
-		fi, err := osStat(target)
37
-		if err != nil {
38
-			if osIsNotExist(err) {
39
-				utils.Errorf("There are no more loopback device available.")
40
-			}
41
-			return nil, ErrAttachLoopbackDevice
42
-		}
43
-
44
-		if fi.Mode()&osModeDevice != osModeDevice {
45
-			utils.Errorf("Loopback device %s is not a block device.", target)
46
-			continue
47
-		}
48
-
49
-		// Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC)
50
-		loopFile, err = osOpenFile(target, osORdWr, 0644)
51
-		if err != nil {
52
-			utils.Errorf("Error openning loopback device: %s", err)
53
-			return nil, ErrAttachLoopbackDevice
54
-		}
55
-
56
-		// Try to attach to the loop file
57
-		if err := ioctlLoopSetFd(loopFile.Fd(), sparseFile.Fd()); err != nil {
58
-			loopFile.Close()
59
-
60
-			// If the error is EBUSY, then try the next loopback
61
-			if err != sysEBusy {
62
-				utils.Errorf("Cannot set up loopback device %s: %s", target, err)
63
-				return nil, ErrAttachLoopbackDevice
64
-			}
65
-
66
-			// Otherwise, we keep going with the loop
67
-			continue
68
-		}
69
-		// In case of success, we finished. Break the loop.
70
-		break
71
-	}
72
-
73
-	// This can't happen, but let's be sure
74
-	if loopFile == nil {
75
-		utils.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name())
76
-		return nil, ErrAttachLoopbackDevice
77
-	}
78
-
79
-	return loopFile, nil
80
-}
81
-
82
-// attachLoopDevice attaches the given sparse file to the next
83
-// available loopback device. It returns an opened *osFile.
84
-func attachLoopDevice(sparseName string) (loop *osFile, err error) {
85
-
86
-	// Try to retrieve the next available loopback device via syscall.
87
-	// If it fails, we discard error and start loopking for a
88
-	// loopback from index 0.
89
-	startIndex, err := getNextFreeLoopbackIndex()
90
-	if err != nil {
91
-		utils.Debugf("Error retrieving the next available loopback: %s", err)
92
-	}
93
-
94
-	// Open the given sparse file (use OpenFile because Open sets O_CLOEXEC)
95
-	sparseFile, err := osOpenFile(sparseName, osORdWr, 0644)
96
-	if err != nil {
97
-		utils.Errorf("Error openning sparse file %s: %s", sparseName, err)
98
-		return nil, ErrAttachLoopbackDevice
99
-	}
100
-	defer sparseFile.Close()
101
-
102
-	loopFile, err := openNextAvailableLoopback(startIndex, sparseFile)
103
-	if err != nil {
104
-		return nil, err
105
-	}
106
-
107
-	// Set the status of the loopback device
108
-	loopInfo := &LoopInfo64{
109
-		loFileName: stringToLoopName(loopFile.Name()),
110
-		loOffset:   0,
111
-		loFlags:    LoFlagsAutoClear,
112
-	}
113
-
114
-	if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil {
115
-		utils.Errorf("Cannot set up loopback device info: %s", err)
116
-
117
-		// If the call failed, then free the loopback device
118
-		if err := ioctlLoopClrFd(loopFile.Fd()); err != nil {
119
-			utils.Errorf("Error while cleaning up the loopback device")
120
-		}
121
-		loopFile.Close()
122
-		return nil, ErrAttachLoopbackDevice
123
-	}
124
-
125
-	return loopFile, nil
126
-}
127 1
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+// +build linux
1
+
2
+package devmapper
3
+
4
+import (
5
+	"fmt"
6
+	"github.com/dotcloud/docker/utils"
7
+)
8
+
9
+func stringToLoopName(src string) [LoNameSize]uint8 {
10
+	var dst [LoNameSize]uint8
11
+	copy(dst[:], src[:])
12
+	return dst
13
+}
14
+
15
+func getNextFreeLoopbackIndex() (int, error) {
16
+	f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644)
17
+	if err != nil {
18
+		return 0, err
19
+	}
20
+	defer f.Close()
21
+
22
+	index, err := ioctlLoopCtlGetFree(f.Fd())
23
+	if index < 0 {
24
+		index = 0
25
+	}
26
+	return index, err
27
+}
28
+
29
+func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, err error) {
30
+	// Start looking for a free /dev/loop
31
+	for {
32
+		target := fmt.Sprintf("/dev/loop%d", index)
33
+		index++
34
+
35
+		fi, err := osStat(target)
36
+		if err != nil {
37
+			if osIsNotExist(err) {
38
+				utils.Errorf("There are no more loopback device available.")
39
+			}
40
+			return nil, ErrAttachLoopbackDevice
41
+		}
42
+
43
+		if fi.Mode()&osModeDevice != osModeDevice {
44
+			utils.Errorf("Loopback device %s is not a block device.", target)
45
+			continue
46
+		}
47
+
48
+		// OpenFile adds O_CLOEXEC
49
+		loopFile, err = osOpenFile(target, osORdWr, 0644)
50
+		if err != nil {
51
+			utils.Errorf("Error openning loopback device: %s", err)
52
+			return nil, ErrAttachLoopbackDevice
53
+		}
54
+
55
+		// Try to attach to the loop file
56
+		if err := ioctlLoopSetFd(loopFile.Fd(), sparseFile.Fd()); err != nil {
57
+			loopFile.Close()
58
+
59
+			// If the error is EBUSY, then try the next loopback
60
+			if err != sysEBusy {
61
+				utils.Errorf("Cannot set up loopback device %s: %s", target, err)
62
+				return nil, ErrAttachLoopbackDevice
63
+			}
64
+
65
+			// Otherwise, we keep going with the loop
66
+			continue
67
+		}
68
+		// In case of success, we finished. Break the loop.
69
+		break
70
+	}
71
+
72
+	// This can't happen, but let's be sure
73
+	if loopFile == nil {
74
+		utils.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name())
75
+		return nil, ErrAttachLoopbackDevice
76
+	}
77
+
78
+	return loopFile, nil
79
+}
80
+
81
+// attachLoopDevice attaches the given sparse file to the next
82
+// available loopback device. It returns an opened *osFile.
83
+func attachLoopDevice(sparseName string) (loop *osFile, err error) {
84
+
85
+	// Try to retrieve the next available loopback device via syscall.
86
+	// If it fails, we discard error and start loopking for a
87
+	// loopback from index 0.
88
+	startIndex, err := getNextFreeLoopbackIndex()
89
+	if err != nil {
90
+		utils.Debugf("Error retrieving the next available loopback: %s", err)
91
+	}
92
+
93
+	// OpenFile adds O_CLOEXEC
94
+	sparseFile, err := osOpenFile(sparseName, osORdWr, 0644)
95
+	if err != nil {
96
+		utils.Errorf("Error openning sparse file %s: %s", sparseName, err)
97
+		return nil, ErrAttachLoopbackDevice
98
+	}
99
+	defer sparseFile.Close()
100
+
101
+	loopFile, err := openNextAvailableLoopback(startIndex, sparseFile)
102
+	if err != nil {
103
+		return nil, err
104
+	}
105
+
106
+	// Set the status of the loopback device
107
+	loopInfo := &LoopInfo64{
108
+		loFileName: stringToLoopName(loopFile.Name()),
109
+		loOffset:   0,
110
+		loFlags:    LoFlagsAutoClear,
111
+	}
112
+
113
+	if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil {
114
+		utils.Errorf("Cannot set up loopback device info: %s", err)
115
+
116
+		// If the call failed, then free the loopback device
117
+		if err := ioctlLoopClrFd(loopFile.Fd()); err != nil {
118
+			utils.Errorf("Error while cleaning up the loopback device")
119
+		}
120
+		loopFile.Close()
121
+		return nil, ErrAttachLoopbackDevice
122
+	}
123
+
124
+	return loopFile, nil
125
+}
... ...
@@ -56,21 +56,24 @@ type (
56 56
 )
57 57
 
58 58
 // FIXME: Make sure the values are defined in C
59
+// IOCTL consts
59 60
 const (
61
+	BlkGetSize64 = C.BLKGETSIZE64
62
+
60 63
 	LoopSetFd       = C.LOOP_SET_FD
61 64
 	LoopCtlGetFree  = C.LOOP_CTL_GET_FREE
62 65
 	LoopGetStatus64 = C.LOOP_GET_STATUS64
63 66
 	LoopSetStatus64 = C.LOOP_SET_STATUS64
64 67
 	LoopClrFd       = C.LOOP_CLR_FD
65 68
 	LoopSetCapacity = C.LOOP_SET_CAPACITY
69
+)
66 70
 
71
+const (
67 72
 	LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR
68 73
 	LoFlagsReadOnly  = C.LO_FLAGS_READ_ONLY
69 74
 	LoFlagsPartScan  = C.LO_FLAGS_PARTSCAN
70 75
 	LoKeySize        = C.LO_KEY_SIZE
71 76
 	LoNameSize       = C.LO_NAME_SIZE
72
-
73
-	BlkGetSize64 = C.BLKGETSIZE64
74 77
 )
75 78
 
76 79
 var (
... ...
@@ -57,12 +57,6 @@ func denyAllDevmapper() {
57 57
 	DmGetNextTarget = func(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
58 58
 		panic("DmGetNextTarget: this method should not be called here")
59 59
 	}
60
-	DmAttachLoopDevice = func(filename string, fd *int) string {
61
-		panic("DmAttachLoopDevice: this method should not be called here")
62
-	}
63
-	DmGetBlockSize = func(fd uintptr) (int64, sysErrno) {
64
-		panic("DmGetBlockSize: this method should not be called here")
65
-	}
66 60
 	DmUdevWait = func(cookie uint) int {
67 61
 		panic("DmUdevWait: this method should not be called here")
68 62
 	}
... ...
@@ -78,9 +72,6 @@ func denyAllDevmapper() {
78 78
 	DmTaskDestroy = func(task *CDmTask) {
79 79
 		panic("DmTaskDestroy: this method should not be called here")
80 80
 	}
81
-	GetBlockSize = func(fd uintptr, size *uint64) sysErrno {
82
-		panic("GetBlockSize: this method should not be called here")
83
-	}
84 81
 	LogWithErrnoInit = func() {
85 82
 		panic("LogWithErrnoInit: this method should not be called here")
86 83
 	}
... ...
@@ -157,11 +148,10 @@ func (r Set) Assert(t *testing.T, names ...string) {
157 157
 
158 158
 func TestInit(t *testing.T) {
159 159
 	var (
160
-		calls           = make(Set)
161
-		devicesAttached = make(Set)
162
-		taskMessages    = make(Set)
163
-		taskTypes       = make(Set)
164
-		home            = mkTestDirectory(t)
160
+		calls        = make(Set)
161
+		taskMessages = make(Set)
162
+		taskTypes    = make(Set)
163
+		home         = mkTestDirectory(t)
165 164
 	)
166 165
 	defer osRemoveAll(home)
167 166
 
... ...
@@ -235,29 +225,6 @@ func TestInit(t *testing.T) {
235 235
 			taskMessages[message] = true
236 236
 			return 1
237 237
 		}
238
-		var (
239
-			fakeDataLoop       = "/dev/loop42"
240
-			fakeMetadataLoop   = "/dev/loop43"
241
-			fakeDataLoopFd     = 42
242
-			fakeMetadataLoopFd = 43
243
-		)
244
-		var attachCount int
245
-		DmAttachLoopDevice = func(filename string, fd *int) string {
246
-			calls["DmAttachLoopDevice"] = true
247
-			if _, exists := devicesAttached[filename]; exists {
248
-				t.Fatalf("Already attached %s", filename)
249
-			}
250
-			devicesAttached[filename] = true
251
-			// This will crash if fd is not dereferenceable
252
-			if attachCount == 0 {
253
-				attachCount++
254
-				*fd = fakeDataLoopFd
255
-				return fakeDataLoop
256
-			} else {
257
-				*fd = fakeMetadataLoopFd
258
-				return fakeMetadataLoop
259
-			}
260
-		}
261 238
 		DmTaskDestroy = func(task *CDmTask) {
262 239
 			calls["DmTaskDestroy"] = true
263 240
 			expectedTask := &task1
... ...
@@ -265,14 +232,6 @@ func TestInit(t *testing.T) {
265 265
 				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
266 266
 			}
267 267
 		}
268
-		fakeBlockSize := int64(4242 * 512)
269
-		DmGetBlockSize = func(fd uintptr) (int64, sysErrno) {
270
-			calls["DmGetBlockSize"] = true
271
-			if expectedFd := uintptr(42); fd != expectedFd {
272
-				t.Fatalf("Wrong libdevmapper call\nExpected: DmGetBlockSize(%v)\nReceived: DmGetBlockSize(%v)\n", expectedFd, fd)
273
-			}
274
-			return fakeBlockSize, 0
275
-		}
276 268
 		DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
277 269
 			calls["DmTaskSetTarget"] = true
278 270
 			expectedTask := &task1
... ...
@@ -347,11 +306,9 @@ func TestInit(t *testing.T) {
347 347
 		"DmTaskSetName",
348 348
 		"DmTaskRun",
349 349
 		"DmTaskGetInfo",
350
-		"DmAttachLoopDevice",
351 350
 		"DmTaskDestroy",
352 351
 		"execRun",
353 352
 		"DmTaskCreate",
354
-		"DmGetBlockSize",
355 353
 		"DmTaskSetTarget",
356 354
 		"DmTaskSetCookie",
357 355
 		"DmUdevWait",
... ...
@@ -359,7 +316,6 @@ func TestInit(t *testing.T) {
359 359
 		"DmTaskSetMessage",
360 360
 		"DmTaskSetAddNode",
361 361
 	)
362
-	devicesAttached.Assert(t, path.Join(home, "devicemapper", "data"), path.Join(home, "devicemapper", "metadata"))
363 362
 	taskTypes.Assert(t, "0", "6", "17")
364 363
 	taskMessages.Assert(t, "create_thin 0", "set_transaction_id 0 1")
365 364
 }
... ...
@@ -410,17 +366,9 @@ func mockAllDevmapper(calls Set) {
410 410
 		calls["DmTaskSetMessage"] = true
411 411
 		return 1
412 412
 	}
413
-	DmAttachLoopDevice = func(filename string, fd *int) string {
414
-		calls["DmAttachLoopDevice"] = true
415
-		return "/dev/loop42"
416
-	}
417 413
 	DmTaskDestroy = func(task *CDmTask) {
418 414
 		calls["DmTaskDestroy"] = true
419 415
 	}
420
-	DmGetBlockSize = func(fd uintptr) (int64, sysErrno) {
421
-		calls["DmGetBlockSize"] = true
422
-		return int64(4242 * 512), 0
423
-	}
424 416
 	DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
425 417
 		calls["DmTaskSetTarget"] = true
426 418
 		return 1
... ...
@@ -491,6 +439,32 @@ func TestDriverCreate(t *testing.T) {
491 491
 		return false, nil
492 492
 	}
493 493
 
494
+	sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
495
+		calls["sysSyscall"] = true
496
+		if trap != sysSysIoctl {
497
+			t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap)
498
+		}
499
+		switch a2 {
500
+		case LoopSetFd:
501
+			calls["ioctl.loopsetfd"] = true
502
+		case LoopCtlGetFree:
503
+			calls["ioctl.loopctlgetfree"] = true
504
+		case LoopGetStatus64:
505
+			calls["ioctl.loopgetstatus"] = true
506
+		case LoopSetStatus64:
507
+			calls["ioctl.loopsetstatus"] = true
508
+		case LoopClrFd:
509
+			calls["ioctl.loopclrfd"] = true
510
+		case LoopSetCapacity:
511
+			calls["ioctl.loopsetcapacity"] = true
512
+		case BlkGetSize64:
513
+			calls["ioctl.blkgetsize"] = true
514
+		default:
515
+			t.Fatalf("Unexpected IOCTL. Received %d", a2)
516
+		}
517
+		return 0, 0, 0
518
+	}
519
+
494 520
 	func() {
495 521
 		d := newDriver(t)
496 522
 
... ...
@@ -500,16 +474,18 @@ func TestDriverCreate(t *testing.T) {
500 500
 			"DmTaskSetName",
501 501
 			"DmTaskRun",
502 502
 			"DmTaskGetInfo",
503
-			"DmAttachLoopDevice",
504 503
 			"execRun",
505 504
 			"DmTaskCreate",
506
-			"DmGetBlockSize",
507 505
 			"DmTaskSetTarget",
508 506
 			"DmTaskSetCookie",
509 507
 			"DmUdevWait",
510 508
 			"DmTaskSetSector",
511 509
 			"DmTaskSetMessage",
512 510
 			"DmTaskSetAddNode",
511
+			"sysSyscall",
512
+			"ioctl.blkgetsize",
513
+			"ioctl.loopsetfd",
514
+			"ioctl.loopsetstatus",
513 515
 		)
514 516
 
515 517
 		if err := d.Create("1", ""); err != nil {
... ...
@@ -581,6 +557,32 @@ func TestDriverRemove(t *testing.T) {
581 581
 		return false, nil
582 582
 	}
583 583
 
584
+	sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
585
+		calls["sysSyscall"] = true
586
+		if trap != sysSysIoctl {
587
+			t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap)
588
+		}
589
+		switch a2 {
590
+		case LoopSetFd:
591
+			calls["ioctl.loopsetfd"] = true
592
+		case LoopCtlGetFree:
593
+			calls["ioctl.loopctlgetfree"] = true
594
+		case LoopGetStatus64:
595
+			calls["ioctl.loopgetstatus"] = true
596
+		case LoopSetStatus64:
597
+			calls["ioctl.loopsetstatus"] = true
598
+		case LoopClrFd:
599
+			calls["ioctl.loopclrfd"] = true
600
+		case LoopSetCapacity:
601
+			calls["ioctl.loopsetcapacity"] = true
602
+		case BlkGetSize64:
603
+			calls["ioctl.blkgetsize"] = true
604
+		default:
605
+			t.Fatalf("Unexpected IOCTL. Received %d", a2)
606
+		}
607
+		return 0, 0, 0
608
+	}
609
+
584 610
 	func() {
585 611
 		d := newDriver(t)
586 612
 
... ...
@@ -590,16 +592,18 @@ func TestDriverRemove(t *testing.T) {
590 590
 			"DmTaskSetName",
591 591
 			"DmTaskRun",
592 592
 			"DmTaskGetInfo",
593
-			"DmAttachLoopDevice",
594 593
 			"execRun",
595 594
 			"DmTaskCreate",
596
-			"DmGetBlockSize",
597 595
 			"DmTaskSetTarget",
598 596
 			"DmTaskSetCookie",
599 597
 			"DmUdevWait",
600 598
 			"DmTaskSetSector",
601 599
 			"DmTaskSetMessage",
602 600
 			"DmTaskSetAddNode",
601
+			"sysSyscall",
602
+			"ioctl.blkgetsize",
603
+			"ioctl.loopsetfd",
604
+			"ioctl.loopsetstatus",
603 605
 		)
604 606
 
605 607
 		if err := d.Create("1", ""); err != nil {
... ...
@@ -36,9 +36,7 @@ var (
36 36
 	osRename     = os.Rename
37 37
 	osReadlink   = os.Readlink
38 38
 
39
-	execRun = func(name string, args ...string) error {
40
-		return exec.Command(name, args...).Run()
41
-	}
39
+	execRun = func(name string, args ...string) error { return exec.Command(name, args...).Run() }
42 40
 )
43 41
 
44 42
 const (
... ...
@@ -19,18 +19,35 @@ fi
19 19
 bundle_test() {
20 20
 	{
21 21
 		date
22
-		for test_dir in $(find_test_dirs); do (
23
-			set -x
24
-			cd $test_dir
22
+		
23
+		TESTS_FAILED=()
24
+		for test_dir in $(find_test_dirs); do
25
+			echo
25 26
 			
26
-			# Install packages that are dependencies of the tests.
27
-			#   Note: Does not run the tests.
28
-			go test -i -ldflags "$LDFLAGS" $BUILDFLAGS
29
-			
30
-			# Run the tests with the optional $TESTFLAGS.
31
-			export TEST_DOCKERINIT_PATH=$DEST/../dynbinary/dockerinit-$VERSION
32
-			go test -v -ldflags "$LDFLAGS -X github.com/dotcloud/docker/utils.INITSHA1 \"$DOCKER_INITSHA1\"" $BUILDFLAGS $TESTFLAGS
33
-		)  done
27
+			if ! (
28
+				set -x
29
+				cd $test_dir
30
+				
31
+				# Install packages that are dependencies of the tests.
32
+				#   Note: Does not run the tests.
33
+				go test -i -ldflags "$LDFLAGS" $BUILDFLAGS
34
+				
35
+				# Run the tests with the optional $TESTFLAGS.
36
+				export TEST_DOCKERINIT_PATH=$DEST/../dynbinary/dockerinit-$VERSION
37
+				go test -v -ldflags "$LDFLAGS -X github.com/dotcloud/docker/utils.INITSHA1 \"$DOCKER_INITSHA1\"" $BUILDFLAGS $TESTFLAGS
38
+			); then
39
+				TESTS_FAILED+=("$test_dir")
40
+				sleep 1 # give it a second, so observers watching can take note
41
+			fi
42
+		done
43
+		
44
+		# if some tests fail, we want the bundlescript to fail, but we want to
45
+		# try running ALL the tests first, hence TESTS_FAILED
46
+		if [ "${#TESTS_FAILED[@]}" -gt 0 ]; then
47
+			echo
48
+			echo "Test failures in: ${TESTS_FAILED[@]}"
49
+			false
50
+		fi
34 51
 	} 2>&1 | tee $DEST/test.log
35 52
 }
36 53
 
... ...
@@ -13,17 +13,34 @@ set -e
13 13
 bundle_test() {
14 14
 	{
15 15
 		date
16
-		for test_dir in $(find_test_dirs); do (
17
-			set -x
18
-			cd $test_dir
16
+		
17
+		TESTS_FAILED=()
18
+		for test_dir in $(find_test_dirs); do
19
+			echo
19 20
 			
20
-			# Install packages that are dependencies of the tests.
21
-			#   Note: Does not run the tests.
22
-			go test -i -ldflags "$LDFLAGS $LDFLAGS_STATIC" $BUILDFLAGS
23
-			
24
-			# Run the tests with the optional $TESTFLAGS.
25
-			go test -v -ldflags "$LDFLAGS $LDFLAGS_STATIC" $BUILDFLAGS $TESTFLAGS
26
-		)  done
21
+			if ! (
22
+				set -x
23
+				cd $test_dir
24
+				
25
+				# Install packages that are dependencies of the tests.
26
+				#   Note: Does not run the tests.
27
+				go test -i -ldflags "$LDFLAGS $LDFLAGS_STATIC" $BUILDFLAGS
28
+				
29
+				# Run the tests with the optional $TESTFLAGS.
30
+				go test -v -ldflags "$LDFLAGS $LDFLAGS_STATIC" $BUILDFLAGS $TESTFLAGS
31
+			); then
32
+				TESTS_FAILED+=("$test_dir")
33
+				sleep 1 # give it a second, so observers watching can take note
34
+			fi
35
+		done
36
+		
37
+		# if some tests fail, we want the bundlescript to fail, but we want to
38
+		# try running ALL the tests first, hence TESTS_FAILED
39
+		if [ "${#TESTS_FAILED[@]}" -gt 0 ]; then
40
+			echo
41
+			echo "Test failures in: ${TESTS_FAILED[@]}"
42
+			false
43
+		fi
27 44
 	} 2>&1 | tee $DEST/test.log
28 45
 }
29 46
 
... ...
@@ -483,7 +483,7 @@ func TestForbiddenContextPath(t *testing.T) {
483 483
 		t.Fail()
484 484
 	}
485 485
 
486
-	if err.Error() != "Forbidden path: /" {
486
+	if err.Error() != "Forbidden path outside the build context: ../../ (/)" {
487 487
 		t.Logf("Error message is not expected: %s", err.Error())
488 488
 		t.Fail()
489 489
 	}
... ...
@@ -1,6 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"strings"
4 5
 	"text/template"
5 6
 )
6 7
 
... ...
@@ -31,8 +32,8 @@ lxc.rootfs = {{$ROOTFS}}
31 31
 
32 32
 {{if and .HostnamePath .HostsPath}}
33 33
 # enable domain name support
34
-lxc.mount.entry = {{.HostnamePath}} {{$ROOTFS}}/etc/hostname none bind,ro 0 0
35
-lxc.mount.entry = {{.HostsPath}} {{$ROOTFS}}/etc/hosts none bind,ro 0 0
34
+lxc.mount.entry = {{escapeFstabSpaces .HostnamePath}} {{escapeFstabSpaces $ROOTFS}}/etc/hostname none bind,ro 0 0
35
+lxc.mount.entry = {{escapeFstabSpaces .HostsPath}} {{escapeFstabSpaces $ROOTFS}}/etc/hosts none bind,ro 0 0
36 36
 {{end}}
37 37
 
38 38
 # use a dedicated pts for the container (and limit the number of pseudo terminal
... ...
@@ -84,27 +85,27 @@ lxc.cgroup.devices.allow = c 10:200 rwm
84 84
 lxc.pivotdir = lxc_putold
85 85
 #  WARNING: procfs is a known attack vector and should probably be disabled
86 86
 #           if your userspace allows it. eg. see http://blog.zx2c4.com/749
87
-lxc.mount.entry = proc {{$ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
87
+lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
88 88
 #  WARNING: sysfs is a known attack vector and should probably be disabled
89 89
 #           if your userspace allows it. eg. see http://bit.ly/T9CkqJ
90
-lxc.mount.entry = sysfs {{$ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
91
-lxc.mount.entry = devpts {{$ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
92
-#lxc.mount.entry = varrun {{$ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0
93
-#lxc.mount.entry = varlock {{$ROOTFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0
94
-lxc.mount.entry = shm {{$ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
90
+lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
91
+lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
92
+#lxc.mount.entry = varrun {{escapeFstabSpaces $ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0
93
+#lxc.mount.entry = varlock {{escapeFstabSpaces $ROOTFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0
94
+lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
95 95
 
96 96
 # Inject dockerinit
97
-lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/.dockerinit none bind,ro 0 0
97
+lxc.mount.entry = {{escapeFstabSpaces .SysInitPath}} {{escapeFstabSpaces $ROOTFS}}/.dockerinit none bind,ro 0 0
98 98
 
99 99
 # Inject env
100
-lxc.mount.entry = {{.EnvConfigPath}} {{$ROOTFS}}/.dockerenv none bind,ro 0 0
100
+lxc.mount.entry = {{escapeFstabSpaces .EnvConfigPath}} {{escapeFstabSpaces $ROOTFS}}/.dockerenv none bind,ro 0 0
101 101
 
102 102
 # In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
103
-lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
103
+lxc.mount.entry = {{escapeFstabSpaces .ResolvConfPath}} {{escapeFstabSpaces $ROOTFS}}/etc/resolv.conf none bind,ro 0 0
104 104
 {{if .Volumes}}
105 105
 {{ $rw := .VolumesRW }}
106 106
 {{range $virtualPath, $realPath := .Volumes}}
107
-lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,{{ if index $rw $virtualPath }}rw{{else}}ro{{end}} 0 0
107
+lxc.mount.entry = {{escapeFstabSpaces $realPath}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $virtualPath}} none bind,{{ if index $rw $virtualPath }}rw{{else}}ro{{end}} 0 0
108 108
 {{end}}
109 109
 {{end}}
110 110
 
... ...
@@ -144,6 +145,12 @@ lxc.cgroup.cpu.shares = {{.Config.CpuShares}}
144 144
 
145 145
 var LxcTemplateCompiled *template.Template
146 146
 
147
+// Escape spaces in strings according to the fstab documentation, which is the
148
+// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab".
149
+func escapeFstabSpaces(field string) string {
150
+	return strings.Replace(field, " ", "\\040", -1)
151
+}
152
+
147 153
 func getMemorySwap(config *Config) int64 {
148 154
 	// By default, MemorySwap is set to twice the size of RAM.
149 155
 	// If you want to omit MemorySwap, set it to `-1'.
... ...
@@ -164,9 +171,10 @@ func getCapabilities(container *Container) *Capabilities {
164 164
 func init() {
165 165
 	var err error
166 166
 	funcMap := template.FuncMap{
167
-		"getMemorySwap":   getMemorySwap,
168
-		"getHostConfig":   getHostConfig,
169
-		"getCapabilities": getCapabilities,
167
+		"getMemorySwap":     getMemorySwap,
168
+		"getHostConfig":     getHostConfig,
169
+		"getCapabilities":   getCapabilities,
170
+		"escapeFstabSpaces": escapeFstabSpaces,
170 171
 	}
171 172
 	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
172 173
 	if err != nil {
... ...
@@ -100,3 +100,21 @@ func grepFile(t *testing.T, path string, pattern string) {
100 100
 	}
101 101
 	t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
102 102
 }
103
+
104
+func TestEscapeFstabSpaces(t *testing.T) {
105
+	var testInputs = map[string]string{
106
+		" ":                      "\\040",
107
+		"":                       "",
108
+		"/double  space":         "/double\\040\\040space",
109
+		"/some long test string": "/some\\040long\\040test\\040string",
110
+		"/var/lib/docker":        "/var/lib/docker",
111
+		" leading":               "\\040leading",
112
+		"trailing ":              "trailing\\040",
113
+	}
114
+	for in, exp := range testInputs {
115
+		if out := escapeFstabSpaces(in); exp != out {
116
+			t.Logf("Expected %s got %s", exp, out)
117
+			t.Fail()
118
+		}
119
+	}
120
+}
... ...
@@ -1011,16 +1011,9 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut
1011 1011
 		localName = remoteName
1012 1012
 	}
1013 1013
 
1014
-	err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf, parallel)
1015
-	if err == registry.ErrLoginRequired {
1014
+	if err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf, parallel); err != nil {
1016 1015
 		return err
1017 1016
 	}
1018
-	if err != nil {
1019
-		if err := srv.pullImage(r, out, remoteName, endpoint, nil, sf); err != nil {
1020
-			return err
1021
-		}
1022
-		return nil
1023
-	}
1024 1017
 
1025 1018
 	return nil
1026 1019
 }
... ...
@@ -1417,19 +1410,15 @@ func (srv *Server) ContainerDestroy(name string, removeVolume, removeLink bool)
1417 1417
 
1418 1418
 var ErrImageReferenced = errors.New("Image referenced by a repository")
1419 1419
 
1420
-func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error {
1420
+func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi, byParents map[string][]*Image) error {
1421 1421
 	// If the image is referenced by a repo, do not delete
1422 1422
 	if len(srv.runtime.repositories.ByID()[id]) != 0 {
1423 1423
 		return ErrImageReferenced
1424 1424
 	}
1425 1425
 	// If the image is not referenced but has children, go recursive
1426 1426
 	referenced := false
1427
-	byParents, err := srv.runtime.graph.ByParent()
1428
-	if err != nil {
1429
-		return err
1430
-	}
1431 1427
 	for _, img := range byParents[id] {
1432
-		if err := srv.deleteImageAndChildren(img.ID, imgs); err != nil {
1428
+		if err := srv.deleteImageAndChildren(img.ID, imgs, byParents); err != nil {
1433 1429
 			if err != ErrImageReferenced {
1434 1430
 				return err
1435 1431
 			}
... ...
@@ -1441,7 +1430,7 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error {
1441 1441
 	}
1442 1442
 
1443 1443
 	// If the image is not referenced and has no children, remove it
1444
-	byParents, err = srv.runtime.graph.ByParent()
1444
+	byParents, err := srv.runtime.graph.ByParent()
1445 1445
 	if err != nil {
1446 1446
 		return err
1447 1447
 	}
... ...
@@ -1466,8 +1455,12 @@ func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error {
1466 1466
 		if err != nil {
1467 1467
 			return err
1468 1468
 		}
1469
+		byParents, err := srv.runtime.graph.ByParent()
1470
+		if err != nil {
1471
+			return err
1472
+		}
1469 1473
 		// Remove all children images
1470
-		if err := srv.deleteImageAndChildren(img.Parent, imgs); err != nil {
1474
+		if err := srv.deleteImageAndChildren(img.Parent, imgs, byParents); err != nil {
1471 1475
 			return err
1472 1476
 		}
1473 1477
 		return srv.deleteImageParents(parent, imgs)
... ...
@@ -1509,7 +1502,7 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro
1509 1509
 		}
1510 1510
 	}
1511 1511
 	if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
1512
-		if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil {
1512
+		if err := srv.deleteImageAndChildren(img.ID, &imgs, nil); err != nil {
1513 1513
 			if err != ErrImageReferenced {
1514 1514
 				return imgs, err
1515 1515
 			}