Browse code

+ Runtime: mount volumes from a host directory with 'docker run -b'

Gabriel Monroy authored on 2013/05/14 08:39:54
Showing 14 changed files
... ...
@@ -551,11 +551,20 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
551 551
 }
552 552
 
553 553
 func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
554
+	hostConfig := &HostConfig{}
555
+
556
+	// allow a nil body for backwards compatibility
557
+	if r.Body != nil {
558
+		if err := json.NewDecoder(r.Body).Decode(hostConfig); err != nil {
559
+			return err
560
+		}
561
+	}
562
+
554 563
 	if vars == nil {
555 564
 		return fmt.Errorf("Missing parameter")
556 565
 	}
557 566
 	name := vars["name"]
558
-	if err := srv.ContainerStart(name); err != nil {
567
+	if err := srv.ContainerStart(name, hostConfig); err != nil {
559 568
 		return err
560 569
 	}
561 570
 	w.WriteHeader(http.StatusNoContent)
... ...
@@ -873,7 +873,8 @@ func TestPostContainersKill(t *testing.T) {
873 873
 	}
874 874
 	defer runtime.Destroy(container)
875 875
 
876
-	if err := container.Start(); err != nil {
876
+	hostConfig := &HostConfig{}
877
+	if err := container.Start(hostConfig); err != nil {
877 878
 		t.Fatal(err)
878 879
 	}
879 880
 
... ...
@@ -917,7 +918,8 @@ func TestPostContainersRestart(t *testing.T) {
917 917
 	}
918 918
 	defer runtime.Destroy(container)
919 919
 
920
-	if err := container.Start(); err != nil {
920
+	hostConfig := &HostConfig{}
921
+	if err := container.Start(hostConfig); err != nil {
921 922
 		t.Fatal(err)
922 923
 	}
923 924
 
... ...
@@ -973,8 +975,15 @@ func TestPostContainersStart(t *testing.T) {
973 973
 	}
974 974
 	defer runtime.Destroy(container)
975 975
 
976
+	hostConfigJSON, err := json.Marshal(&HostConfig{})
977
+
978
+	req, err := http.NewRequest("POST", "/containers/"+container.ID+"/start", bytes.NewReader(hostConfigJSON))
979
+	if err != nil {
980
+		t.Fatal(err)
981
+	}
982
+
976 983
 	r := httptest.NewRecorder()
977
-	if err := postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
984
+	if err := postContainersStart(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
978 985
 		t.Fatal(err)
979 986
 	}
980 987
 	if r.Code != http.StatusNoContent {
... ...
@@ -989,7 +998,7 @@ func TestPostContainersStart(t *testing.T) {
989 989
 	}
990 990
 
991 991
 	r = httptest.NewRecorder()
992
-	if err = postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err == nil {
992
+	if err = postContainersStart(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err == nil {
993 993
 		t.Fatalf("A running containter should be able to be started")
994 994
 	}
995 995
 
... ...
@@ -1019,7 +1028,8 @@ func TestPostContainersStop(t *testing.T) {
1019 1019
 	}
1020 1020
 	defer runtime.Destroy(container)
1021 1021
 
1022
-	if err := container.Start(); err != nil {
1022
+	hostConfig := &HostConfig{}
1023
+	if err := container.Start(hostConfig); err != nil {
1023 1024
 		t.Fatal(err)
1024 1025
 	}
1025 1026
 
... ...
@@ -1068,7 +1078,8 @@ func TestPostContainersWait(t *testing.T) {
1068 1068
 	}
1069 1069
 	defer runtime.Destroy(container)
1070 1070
 
1071
-	if err := container.Start(); err != nil {
1071
+	hostConfig := &HostConfig{}
1072
+	if err := container.Start(hostConfig); err != nil {
1072 1073
 		t.Fatal(err)
1073 1074
 	}
1074 1075
 
... ...
@@ -1113,7 +1124,8 @@ func TestPostContainersAttach(t *testing.T) {
1113 1113
 	defer runtime.Destroy(container)
1114 1114
 
1115 1115
 	// Start the process
1116
-	if err := container.Start(); err != nil {
1116
+	hostConfig := &HostConfig{}
1117
+	if err := container.Start(hostConfig); err != nil {
1117 1118
 		t.Fatal(err)
1118 1119
 	}
1119 1120
 
... ...
@@ -87,7 +87,7 @@ func (b *buildFile) CmdRun(args string) error {
87 87
 	if b.image == "" {
88 88
 		return fmt.Errorf("Please provide a source image with `from` prior to run")
89 89
 	}
90
-	config, _, err := ParseRun([]string{b.image, "/bin/sh", "-c", args}, nil)
90
+	config, _, _, err := ParseRun([]string{b.image, "/bin/sh", "-c", args}, nil)
91 91
 	if err != nil {
92 92
 		return err
93 93
 	}
... ...
@@ -263,7 +263,8 @@ func (b *buildFile) run() (string, error) {
263 263
 	fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID))
264 264
 
265 265
 	//start the container
266
-	if err := c.Start(); err != nil {
266
+	hostConfig := &HostConfig{}
267
+	if err := c.Start(hostConfig); err != nil {
267 268
 		return "", err
268 269
 	}
269 270
 
... ...
@@ -1235,7 +1235,7 @@ func (cli *DockerCli) CmdTag(args ...string) error {
1235 1235
 }
1236 1236
 
1237 1237
 func (cli *DockerCli) CmdRun(args ...string) error {
1238
-	config, cmd, err := ParseRun(args, nil)
1238
+	config, hostConfig, cmd, err := ParseRun(args, nil)
1239 1239
 	if err != nil {
1240 1240
 		return err
1241 1241
 	}
... ...
@@ -1274,7 +1274,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1274 1274
 	}
1275 1275
 
1276 1276
 	//start the container
1277
-	if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", nil); err != nil {
1277
+	if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil {
1278 1278
 		return err
1279 1279
 	}
1280 1280
 
... ...
@@ -52,6 +52,8 @@ type Container struct {
52 52
 
53 53
 	waitLock chan struct{}
54 54
 	Volumes  map[string]string
55
+
56
+	Binds []BindMap
55 57
 }
56 58
 
57 59
 type Config struct {
... ...
@@ -75,7 +77,17 @@ type Config struct {
75 75
 	VolumesFrom  string
76 76
 }
77 77
 
78
-func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet, error) {
78
+type HostConfig struct {
79
+	Binds []string
80
+}
81
+
82
+type BindMap struct {
83
+	SrcPath string
84
+	DstPath string
85
+	Mode    string
86
+}
87
+
88
+func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
79 89
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
80 90
 	if len(args) > 0 && args[0] != "--help" {
81 91
 		cmd.SetOutput(ioutil.Discard)
... ...
@@ -111,11 +123,14 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
111 111
 
112 112
 	flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
113 113
 
114
+	var flBinds ListOpts
115
+	cmd.Var(&flBinds, "b", "Bind mount a volume from the host (e.g. -b /host:/container)")
116
+
114 117
 	if err := cmd.Parse(args); err != nil {
115
-		return nil, cmd, err
118
+		return nil, nil, cmd, err
116 119
 	}
117 120
 	if *flDetach && len(flAttach) > 0 {
118
-		return nil, cmd, fmt.Errorf("Conflicting options: -a and -d")
121
+		return nil, nil, cmd, fmt.Errorf("Conflicting options: -a and -d")
119 122
 	}
120 123
 	// If neither -d or -a are set, attach to everything by default
121 124
 	if len(flAttach) == 0 && !*flDetach {
... ...
@@ -127,6 +142,15 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
127 127
 			}
128 128
 		}
129 129
 	}
130
+
131
+	// add any bind targets to the list of container volumes
132
+	type empty struct{}
133
+	for _, bind := range flBinds {
134
+		arr := strings.Split(bind, ":")
135
+		dstDir := arr[1]
136
+		flVolumes[dstDir] = empty{}
137
+	}
138
+
130 139
 	parsedArgs := cmd.Args()
131 140
 	runCmd := []string{}
132 141
 	image := ""
... ...
@@ -154,6 +178,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
154 154
 		Volumes:      flVolumes,
155 155
 		VolumesFrom:  *flVolumesFrom,
156 156
 	}
157
+	hostConfig := &HostConfig{
158
+		Binds: flBinds,
159
+	}
157 160
 
158 161
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
159 162
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
... ...
@@ -164,7 +191,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
164 164
 	if config.OpenStdin && config.AttachStdin {
165 165
 		config.StdinOnce = true
166 166
 	}
167
-	return config, cmd, nil
167
+	return config, hostConfig, cmd, nil
168 168
 }
169 169
 
170 170
 type NetworkSettings struct {
... ...
@@ -430,7 +457,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
430 430
 	})
431 431
 }
432 432
 
433
-func (container *Container) Start() error {
433
+func (container *Container) Start(hostConfig *HostConfig) error {
434 434
 	container.State.lock()
435 435
 	defer container.State.unlock()
436 436
 
... ...
@@ -483,6 +510,42 @@ func (container *Container) Start() error {
483 483
 		}
484 484
 	}
485 485
 
486
+	// Create the requested bind mounts
487
+	binds := []BindMap{}
488
+	// Define illegal container destinations
489
+	illegal_dsts := []string{"/", "."}
490
+
491
+	for _, bind := range hostConfig.Binds {
492
+		var src, dst, mode string
493
+		arr := strings.Split(bind, ":")
494
+		if len(arr) == 2 {
495
+			src = arr[0]
496
+			dst = arr[1]
497
+			mode = "rw"
498
+		} else if len(arr) == 3 {
499
+			src = arr[0]
500
+			dst = arr[1]
501
+			mode = arr[2]
502
+		} else {
503
+			return fmt.Errorf("Invalid bind specification: %s", bind)
504
+		}
505
+
506
+		// Bail if trying to mount to an illegal destination
507
+		for _, illegal := range illegal_dsts {
508
+			if dst == illegal {
509
+				return fmt.Errorf("Illegal bind destination: %s", dst)
510
+			}
511
+		}
512
+
513
+		bindMap := BindMap{
514
+			SrcPath: src,
515
+			DstPath: dst,
516
+			Mode:    mode,
517
+		}
518
+		binds = append(binds, bindMap)
519
+	}
520
+	container.Binds = binds
521
+
486 522
 	if err := container.generateLXCConfig(); err != nil {
487 523
 		return err
488 524
 	}
... ...
@@ -552,7 +615,8 @@ func (container *Container) Start() error {
552 552
 }
553 553
 
554 554
 func (container *Container) Run() error {
555
-	if err := container.Start(); err != nil {
555
+	hostConfig := &HostConfig{}
556
+	if err := container.Start(hostConfig); err != nil {
556 557
 		return err
557 558
 	}
558 559
 	container.Wait()
... ...
@@ -565,7 +629,8 @@ func (container *Container) Output() (output []byte, err error) {
565 565
 		return nil, err
566 566
 	}
567 567
 	defer pipe.Close()
568
-	if err := container.Start(); err != nil {
568
+	hostConfig := &HostConfig{}
569
+	if err := container.Start(hostConfig); err != nil {
569 570
 		return nil, err
570 571
 	}
571 572
 	output, err = ioutil.ReadAll(pipe)
... ...
@@ -768,7 +833,8 @@ func (container *Container) Restart(seconds int) error {
768 768
 	if err := container.Stop(seconds); err != nil {
769 769
 		return err
770 770
 	}
771
-	if err := container.Start(); err != nil {
771
+	hostConfig := &HostConfig{}
772
+	if err := container.Start(hostConfig); err != nil {
772 773
 		return err
773 774
 	}
774 775
 	return nil
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"io/ioutil"
8 8
 	"math/rand"
9 9
 	"os"
10
+	"path"
10 11
 	"regexp"
11 12
 	"sort"
12 13
 	"strings"
... ...
@@ -70,7 +71,8 @@ func TestMultipleAttachRestart(t *testing.T) {
70 70
 	if err != nil {
71 71
 		t.Fatal(err)
72 72
 	}
73
-	if err := container.Start(); err != nil {
73
+	hostConfig := &HostConfig{}
74
+	if err := container.Start(hostConfig); err != nil {
74 75
 		t.Fatal(err)
75 76
 	}
76 77
 	l1, err := bufio.NewReader(stdout1).ReadString('\n')
... ...
@@ -111,7 +113,7 @@ func TestMultipleAttachRestart(t *testing.T) {
111 111
 	if err != nil {
112 112
 		t.Fatal(err)
113 113
 	}
114
-	if err := container.Start(); err != nil {
114
+	if err := container.Start(hostConfig); err != nil {
115 115
 		t.Fatal(err)
116 116
 	}
117 117
 
... ...
@@ -306,7 +308,8 @@ func TestCommitAutoRun(t *testing.T) {
306 306
 	if err != nil {
307 307
 		t.Fatal(err)
308 308
 	}
309
-	if err := container2.Start(); err != nil {
309
+	hostConfig := &HostConfig{}
310
+	if err := container2.Start(hostConfig); err != nil {
310 311
 		t.Fatal(err)
311 312
 	}
312 313
 	container2.Wait()
... ...
@@ -388,7 +391,8 @@ func TestCommitRun(t *testing.T) {
388 388
 	if err != nil {
389 389
 		t.Fatal(err)
390 390
 	}
391
-	if err := container2.Start(); err != nil {
391
+	hostConfig := &HostConfig{}
392
+	if err := container2.Start(hostConfig); err != nil {
392 393
 		t.Fatal(err)
393 394
 	}
394 395
 	container2.Wait()
... ...
@@ -436,7 +440,8 @@ func TestStart(t *testing.T) {
436 436
 		t.Fatal(err)
437 437
 	}
438 438
 
439
-	if err := container.Start(); err != nil {
439
+	hostConfig := &HostConfig{}
440
+	if err := container.Start(hostConfig); err != nil {
440 441
 		t.Fatal(err)
441 442
 	}
442 443
 
... ...
@@ -446,7 +451,7 @@ func TestStart(t *testing.T) {
446 446
 	if !container.State.Running {
447 447
 		t.Errorf("Container should be running")
448 448
 	}
449
-	if err := container.Start(); err == nil {
449
+	if err := container.Start(hostConfig); err == nil {
450 450
 		t.Fatalf("A running containter should be able to be started")
451 451
 	}
452 452
 
... ...
@@ -528,7 +533,8 @@ func TestKillDifferentUser(t *testing.T) {
528 528
 	if container.State.Running {
529 529
 		t.Errorf("Container shouldn't be running")
530 530
 	}
531
-	if err := container.Start(); err != nil {
531
+	hostConfig := &HostConfig{}
532
+	if err := container.Start(hostConfig); err != nil {
532 533
 		t.Fatal(err)
533 534
 	}
534 535
 
... ...
@@ -599,7 +605,8 @@ func TestKill(t *testing.T) {
599 599
 	if container.State.Running {
600 600
 		t.Errorf("Container shouldn't be running")
601 601
 	}
602
-	if err := container.Start(); err != nil {
602
+	hostConfig := &HostConfig{}
603
+	if err := container.Start(hostConfig); err != nil {
603 604
 		t.Fatal(err)
604 605
 	}
605 606
 
... ...
@@ -724,7 +731,8 @@ func TestRestartStdin(t *testing.T) {
724 724
 	if err != nil {
725 725
 		t.Fatal(err)
726 726
 	}
727
-	if err := container.Start(); err != nil {
727
+	hostConfig := &HostConfig{}
728
+	if err := container.Start(hostConfig); err != nil {
728 729
 		t.Fatal(err)
729 730
 	}
730 731
 	if _, err := io.WriteString(stdin, "hello world"); err != nil {
... ...
@@ -754,7 +762,7 @@ func TestRestartStdin(t *testing.T) {
754 754
 	if err != nil {
755 755
 		t.Fatal(err)
756 756
 	}
757
-	if err := container.Start(); err != nil {
757
+	if err := container.Start(hostConfig); err != nil {
758 758
 		t.Fatal(err)
759 759
 	}
760 760
 	if _, err := io.WriteString(stdin, "hello world #2"); err != nil {
... ...
@@ -916,10 +924,11 @@ func TestMultipleContainers(t *testing.T) {
916 916
 	defer runtime.Destroy(container2)
917 917
 
918 918
 	// Start both containers
919
-	if err := container1.Start(); err != nil {
919
+	hostConfig := &HostConfig{}
920
+	if err := container1.Start(hostConfig); err != nil {
920 921
 		t.Fatal(err)
921 922
 	}
922
-	if err := container2.Start(); err != nil {
923
+	if err := container2.Start(hostConfig); err != nil {
923 924
 		t.Fatal(err)
924 925
 	}
925 926
 
... ...
@@ -971,7 +980,8 @@ func TestStdin(t *testing.T) {
971 971
 	if err != nil {
972 972
 		t.Fatal(err)
973 973
 	}
974
-	if err := container.Start(); err != nil {
974
+	hostConfig := &HostConfig{}
975
+	if err := container.Start(hostConfig); err != nil {
975 976
 		t.Fatal(err)
976 977
 	}
977 978
 	defer stdin.Close()
... ...
@@ -1018,7 +1028,8 @@ func TestTty(t *testing.T) {
1018 1018
 	if err != nil {
1019 1019
 		t.Fatal(err)
1020 1020
 	}
1021
-	if err := container.Start(); err != nil {
1021
+	hostConfig := &HostConfig{}
1022
+	if err := container.Start(hostConfig); err != nil {
1022 1023
 		t.Fatal(err)
1023 1024
 	}
1024 1025
 	defer stdin.Close()
... ...
@@ -1060,7 +1071,8 @@ func TestEnv(t *testing.T) {
1060 1060
 		t.Fatal(err)
1061 1061
 	}
1062 1062
 	defer stdout.Close()
1063
-	if err := container.Start(); err != nil {
1063
+	hostConfig := &HostConfig{}
1064
+	if err := container.Start(hostConfig); err != nil {
1064 1065
 		t.Fatal(err)
1065 1066
 	}
1066 1067
 	container.Wait()
... ...
@@ -1196,7 +1208,8 @@ func BenchmarkRunParallel(b *testing.B) {
1196 1196
 				return
1197 1197
 			}
1198 1198
 			defer runtime.Destroy(container)
1199
-			if err := container.Start(); err != nil {
1199
+			hostConfig := &HostConfig{}
1200
+			if err := container.Start(hostConfig); err != nil {
1200 1201
 				complete <- err
1201 1202
 				return
1202 1203
 			}
... ...
@@ -1225,3 +1238,98 @@ func BenchmarkRunParallel(b *testing.B) {
1225 1225
 		b.Fatal(errors)
1226 1226
 	}
1227 1227
 }
1228
+
1229
+func TestBindMounts(t *testing.T) {
1230
+	runtime, err := newTestRuntime()
1231
+	if err != nil {
1232
+		t.Fatal(err)
1233
+	}
1234
+	defer nuke(runtime)
1235
+	tmpDir, err := ioutil.TempDir("", "docker-test")
1236
+	if err != nil {
1237
+		t.Fatal(err)
1238
+	}
1239
+	tmpFile := path.Join(tmpDir, "touch-me")
1240
+	_, err = os.Create(tmpFile)
1241
+	if err != nil {
1242
+		t.Fatal(err)
1243
+	}
1244
+	// test reading from bind mount
1245
+	bind_str := fmt.Sprintf("%s:/tmp:ro", tmpDir)
1246
+	container, err := NewBuilder(runtime).Create(&Config{
1247
+		Image: GetTestImage(runtime).ID,
1248
+		Cmd:   []string{"ls", "/tmp"},
1249
+	},
1250
+	)
1251
+	if err != nil {
1252
+		t.Fatal(err)
1253
+	}
1254
+	defer runtime.Destroy(container)
1255
+
1256
+	stdout, err := container.StdoutPipe()
1257
+	if err != nil {
1258
+		t.Fatal(err)
1259
+	}
1260
+	defer stdout.Close()
1261
+	hostConfig := &HostConfig{
1262
+		Binds: []string{bind_str},
1263
+	}
1264
+	if err := container.Start(hostConfig); err != nil {
1265
+		t.Fatal(err)
1266
+	}
1267
+	container.Wait()
1268
+	output, err := ioutil.ReadAll(stdout)
1269
+	if err != nil {
1270
+		t.Fatal(err)
1271
+	}
1272
+	if !strings.Contains(string(output), "touch-me") {
1273
+		t.Fatal("Container failed to read from bind mount")
1274
+	}
1275
+	// test writing to bind mount
1276
+	bind_str2 := fmt.Sprintf("%s:/tmp:rw", tmpDir)
1277
+	container2, err := NewBuilder(runtime).Create(&Config{
1278
+		Image: GetTestImage(runtime).ID,
1279
+		Cmd:   []string{"touch", "/tmp/holla"},
1280
+	},
1281
+	)
1282
+	if err != nil {
1283
+		t.Fatal(err)
1284
+	}
1285
+	defer runtime.Destroy(container2)
1286
+
1287
+	hostConfig2 := &HostConfig{
1288
+		Binds: []string{bind_str2},
1289
+	}
1290
+	if err := container2.Start(hostConfig2); err != nil {
1291
+		t.Fatal(err)
1292
+	}
1293
+	container2.Wait()
1294
+	_, err = ioutil.ReadFile(tmpDir + "/holla")
1295
+	if err != nil {
1296
+		t.Fatal("Container failed to write to bind mount")
1297
+	}
1298
+	// test mounting to an illegal destination directory
1299
+	bind_str3 := fmt.Sprintf("%s:.", tmpDir)
1300
+	container3, err := NewBuilder(runtime).Create(&Config{
1301
+		Image: GetTestImage(runtime).ID,
1302
+		Cmd:   []string{"ls", "."},
1303
+	},
1304
+	)
1305
+	if err != nil {
1306
+		t.Fatal(err)
1307
+	}
1308
+	defer runtime.Destroy(container3)
1309
+
1310
+	stdout3, err := container3.StdoutPipe()
1311
+	if err != nil {
1312
+		t.Fatal(err)
1313
+	}
1314
+	defer stdout3.Close()
1315
+	hostConfig3 := &HostConfig{
1316
+		Binds: []string{bind_str3},
1317
+	}
1318
+	if err := container3.Start(hostConfig3); err == nil {
1319
+		t.Fatal("Container bind mounted illegal directory")
1320
+	}
1321
+
1322
+}
... ...
@@ -42,6 +42,9 @@ List containers (/containers/json):
42 42
 
43 43
 - You can use size=1 to get the size of the containers
44 44
 
45
+Start containers (/containers/<id>/start):
46
+
47
+- You can now pass host-specific configuration (e.g. bind mounts) in the POST body for start calls 
45 48
 
46 49
 :doc:`docker_remote_api_v1.2`
47 50
 *****************************
... ...
@@ -294,23 +294,30 @@ Start a container
294 294
 
295 295
 .. http:post:: /containers/(id)/start
296 296
 
297
-	Start the container ``id``
297
+        Start the container ``id``
298 298
 
299
-	**Example request**:
299
+        **Example request**:
300 300
 
301
-	.. sourcecode:: http
301
+        .. sourcecode:: http
302 302
 
303
-	   POST /containers/e90e34656806/start HTTP/1.1
304
-	   
305
-	**Example response**:
303
+           POST /containers/(id)/start HTTP/1.1
304
+           Content-Type: application/json
306 305
 
307
-	.. sourcecode:: http
306
+           {
307
+                "Binds":["/tmp:/tmp"]
308
+           }
308 309
 
309
-	   HTTP/1.1 200 OK
310
-	   	
311
-	:statuscode 200: no error
312
-	:statuscode 404: no such container
313
-	:statuscode 500: server error
310
+        **Example response**:
311
+
312
+        .. sourcecode:: http
313
+
314
+           HTTP/1.1 204 No Content
315
+           Content-Type: text/plain
316
+
317
+        :jsonparam hostConfig: the container's host configuration (optional)
318
+        :statuscode 200: no error
319
+        :statuscode 404: no such container
320
+        :statuscode 500: server error
314 321
 
315 322
 
316 323
 Stop a contaier
... ...
@@ -25,3 +25,4 @@
25 25
       -d=[]: Set custom dns servers for the container
26 26
       -v=[]: Creates a new volume and mounts it at the specified path.
27 27
       -volumes-from="": Mount all volumes from the given container.
28
+      -b=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]
... ...
@@ -88,6 +88,10 @@ lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0
88 88
 lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,rw 0 0
89 89
 {{end}}
90 90
 {{end}}
91
+{{if .Binds}}# User-defined bind mounts
92
+{{range $bindMap := .Binds}}lxc.mount.entry = {{$bindMap.SrcPath}} {{$ROOTFS}}{{$bindMap.DstPath}} none bind,{{$bindMap.Mode}} 0 0
93
+{{end}}
94
+{{end}}
91 95
 
92 96
 # drop linux capabilities (apply mainly to the user root in the container)
93 97
 #  (Note: 'lxc.cap.keep' is coming soon and should replace this under the
... ...
@@ -144,7 +144,9 @@ func (runtime *Runtime) Register(container *Container) error {
144 144
 				utils.Debugf("Restarting")
145 145
 				container.State.Ghost = false
146 146
 				container.State.setStopped(0)
147
-				if err := container.Start(); err != nil {
147
+				// assume empty host config
148
+				hostConfig := &HostConfig{}
149
+				if err := container.Start(hostConfig); err != nil {
148 150
 					return err
149 151
 				}
150 152
 				nomonitor = true
... ...
@@ -327,7 +327,8 @@ func findAvailalblePort(runtime *Runtime, port int) (*Container, error) {
327 327
 	if err != nil {
328 328
 		return nil, err
329 329
 	}
330
-	if err := container.Start(); err != nil {
330
+	hostConfig := &HostConfig{}
331
+	if err := container.Start(hostConfig); err != nil {
331 332
 		if strings.Contains(err.Error(), "address already in use") {
332 333
 			return nil, nil
333 334
 		}
... ...
@@ -437,7 +438,8 @@ func TestRestore(t *testing.T) {
437 437
 	defer runtime1.Destroy(container2)
438 438
 
439 439
 	// Start the container non blocking
440
-	if err := container2.Start(); err != nil {
440
+	hostConfig := &HostConfig{}
441
+	if err := container2.Start(hostConfig); err != nil {
441 442
 		t.Fatal(err)
442 443
 	}
443 444
 
... ...
@@ -87,7 +87,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
87 87
 	}
88 88
 	defer file.Body.Close()
89 89
 
90
-	config, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities)
90
+	config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities)
91 91
 	if err != nil {
92 92
 		return "", err
93 93
 	}
... ...
@@ -934,9 +934,9 @@ func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error)
934 934
 	return nil, nil
935 935
 }
936 936
 
937
-func (srv *Server) ContainerStart(name string) error {
937
+func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
938 938
 	if container := srv.runtime.Get(name); container != nil {
939
-		if err := container.Start(); err != nil {
939
+		if err := container.Start(hostConfig); err != nil {
940 940
 			return fmt.Errorf("Error starting container %s: %s", name, err.Error())
941 941
 		}
942 942
 	} else {
... ...
@@ -65,7 +65,7 @@ func TestCreateRm(t *testing.T) {
65 65
 
66 66
 	srv := &Server{runtime: runtime}
67 67
 
68
-	config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
68
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
69 69
 	if err != nil {
70 70
 		t.Fatal(err)
71 71
 	}
... ...
@@ -98,7 +98,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
98 98
 
99 99
 	srv := &Server{runtime: runtime}
100 100
 
101
-	config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "/bin/cat"}, nil)
101
+	config, hostConfig, _, err := ParseRun([]string{GetTestImage(runtime).ID, "/bin/cat"}, nil)
102 102
 	if err != nil {
103 103
 		t.Fatal(err)
104 104
 	}
... ...
@@ -112,7 +112,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
112 112
 		t.Errorf("Expected 1 container, %v found", len(runtime.List()))
113 113
 	}
114 114
 
115
-	err = srv.ContainerStart(id)
115
+	err = srv.ContainerStart(id, hostConfig)
116 116
 	if err != nil {
117 117
 		t.Fatal(err)
118 118
 	}
... ...
@@ -127,7 +127,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
127 127
 		t.Fatal(err)
128 128
 	}
129 129
 
130
-	err = srv.ContainerStart(id)
130
+	err = srv.ContainerStart(id, hostConfig)
131 131
 	if err != nil {
132 132
 		t.Fatal(err)
133 133
 	}