Browse code

Simplified the core container API, ported it to the new graph. Some features are missing eg. image 'paths' and tags

Solomon Hykes authored on 2013/03/21 16:25:00
Showing 7 changed files
... ...
@@ -1,20 +1,16 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"bufio"
5 4
 	"bytes"
6 5
 	"encoding/json"
7 6
 	"errors"
8 7
 	"fmt"
9 8
 	"github.com/dotcloud/docker/auth"
10
-	"github.com/dotcloud/docker/fs"
11 9
 	"github.com/dotcloud/docker/future"
12 10
 	"github.com/dotcloud/docker/rcli"
13 11
 	"io"
14
-	"io/ioutil"
15 12
 	"net/http"
16 13
 	"net/url"
17
-	"os"
18 14
 	"path"
19 15
 	"strconv"
20 16
 	"strings"
... ...
@@ -137,7 +133,7 @@ func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...str
137 137
 
138 138
 // 'docker info': display system-wide information.
139 139
 func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
140
-	images, _ := srv.images.Images()
140
+	images, _ := srv.containers.graph.All()
141 141
 	var imgcount int
142 142
 	if images == nil {
143 143
 		imgcount = 0
... ...
@@ -236,7 +232,7 @@ func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...strin
236 236
 	}
237 237
 	for _, name := range cmd.Args() {
238 238
 		if container := srv.containers.Get(name); container != nil {
239
-			if err := container.Mountpoint.EnsureMounted(); err != nil {
239
+			if err := container.EnsureMounted(); err != nil {
240 240
 				return err
241 241
 			}
242 242
 			fmt.Fprintln(stdout, container.Id)
... ...
@@ -260,7 +256,7 @@ func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...str
260 260
 	var obj interface{}
261 261
 	if container := srv.containers.Get(name); container != nil {
262 262
 		obj = container
263
-	} else if image, err := srv.images.Find(name); err != nil {
263
+	} else if image, err := srv.containers.graph.Get(name); err != nil {
264 264
 		return err
265 265
 	} else if image != nil {
266 266
 		obj = image
... ...
@@ -310,27 +306,12 @@ func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string
310 310
 // 'docker rmi NAME' removes all images with the name NAME
311 311
 func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) (err error) {
312 312
 	cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
313
-	fl_all := cmd.Bool("a", false, "Use IMAGE as a path and remove ALL images in this path")
314
-	fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name")
315 313
 	if cmd.Parse(args) != nil || cmd.NArg() < 1 {
316 314
 		cmd.Usage()
317 315
 		return nil
318 316
 	}
319 317
 	for _, name := range cmd.Args() {
320
-		if *fl_regexp {
321
-			err = srv.images.RemoveRegexp(name)
322
-		} else if *fl_all {
323
-			err = srv.images.RemoveInPath(name)
324
-		} else {
325
-			if image, err1 := srv.images.Find(name); err1 != nil {
326
-				err = err1
327
-			} else if err1 == nil && image == nil {
328
-				err = fmt.Errorf("No such image: %s", name)
329
-			} else {
330
-				err = srv.images.Remove(image)
331
-			}
332
-		}
333
-		if err != nil {
318
+		if err := srv.containers.graph.Delete(name); err != nil {
334 319
 			return err
335 320
 		}
336 321
 	}
... ...
@@ -409,7 +390,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri
409 409
 		archive = future.ProgressReader(resp.Body, int(resp.ContentLength), stdout)
410 410
 	}
411 411
 	fmt.Fprintf(stdout, "Unpacking to %s\n", name)
412
-	img, err := srv.images.Create(archive, nil, name, "")
412
+	img, err := srv.containers.graph.Create(archive, "", "")
413 413
 	if err != nil {
414 414
 		return err
415 415
 	}
... ...
@@ -419,7 +400,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri
419 419
 
420 420
 func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
421 421
 	cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
422
-	limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
422
+	//limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
423 423
 	quiet := cmd.Bool("q", false, "only show numeric IDs")
424 424
 	if err := cmd.Parse(args); err != nil {
425 425
 		return nil
... ...
@@ -428,51 +409,65 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri
428 428
 		cmd.Usage()
429 429
 		return nil
430 430
 	}
431
-	var nameFilter string
432
-	if cmd.NArg() == 1 {
433
-		nameFilter = cmd.Arg(0)
434
-	}
431
+	/*
432
+		var nameFilter string
433
+		if cmd.NArg() == 1 {
434
+			nameFilter = cmd.Arg(0)
435
+		}
436
+	*/
435 437
 	w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
436 438
 	if !*quiet {
437 439
 		fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n")
438 440
 	}
439
-	paths, err := srv.images.Paths()
440
-	if err != nil {
441
-		return err
442
-	}
443
-	for _, name := range paths {
444
-		if nameFilter != "" && nameFilter != name {
445
-			continue
446
-		}
447
-		ids, err := srv.images.List(name)
441
+	if *quiet {
442
+		images, err := srv.containers.graph.All()
448 443
 		if err != nil {
449 444
 			return err
450 445
 		}
451
-		for idx, img := range ids {
452
-			if *limit > 0 && idx >= *limit {
453
-				break
454
-			}
455
-			if !*quiet {
456
-				for idx, field := range []string{
457
-					/* NAME */ name,
458
-					/* ID */ img.Id,
459
-					/* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
460
-					/* PARENT */ img.Parent,
461
-				} {
462
-					if idx == 0 {
463
-						w.Write([]byte(field))
464
-					} else {
465
-						w.Write([]byte("\t" + field))
466
-					}
467
-				}
468
-				w.Write([]byte{'\n'})
469
-			} else {
470
-				stdout.Write([]byte(img.Id + "\n"))
471
-			}
446
+		for _, image := range images {
447
+			fmt.Fprintln(stdout, image.Id)
472 448
 		}
473
-	}
474
-	if !*quiet {
475
-		w.Flush()
449
+	} else {
450
+		// FIXME:
451
+		//		paths, err := srv.images.Paths()
452
+		//		if err != nil {
453
+		//			return err
454
+		//		}
455
+		//		for _, name := range paths {
456
+		//			if nameFilter != "" && nameFilter != name {
457
+		//				continue
458
+		//			}
459
+		//			ids, err := srv.images.List(name)
460
+		//			if err != nil {
461
+		//				return err
462
+		//			}
463
+		//			for idx, img := range ids {
464
+		//				if *limit > 0 && idx >= *limit {
465
+		//					break
466
+		//				}
467
+		//				if !*quiet {
468
+		//					for idx, field := range []string{
469
+		//						/* NAME */ name,
470
+		//						/* ID */ img.Id,
471
+		//						/* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
472
+		//						/* PARENT */ img.Parent,
473
+		//					} {
474
+		//						if idx == 0 {
475
+		//							w.Write([]byte(field))
476
+		//						} else {
477
+		//							w.Write([]byte("\t" + field))
478
+		//						}
479
+		//					}
480
+		//					w.Write([]byte{'\n'})
481
+		//				} else {
482
+		//					stdout.Write([]byte(img.Id + "\n"))
483
+		//				}
484
+		//			}
485
+		//		}
486
+		//		if !*quiet {
487
+		//			w.Flush()
488
+		//		}
489
+		//
476 490
 	}
477 491
 	return nil
478 492
 
... ...
@@ -492,7 +487,6 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
492 492
 		fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n")
493 493
 	}
494 494
 	for _, container := range srv.containers.List() {
495
-		comment := container.GetUserData("comment")
496 495
 		if !container.State.Running && !*fl_all {
497 496
 			continue
498 497
 		}
... ...
@@ -503,11 +497,11 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
503 503
 			}
504 504
 			for idx, field := range []string{
505 505
 				/* ID */ container.Id,
506
-				/* IMAGE */ container.GetUserData("image"),
506
+				/* IMAGE */ container.Image,
507 507
 				/* COMMAND */ command,
508 508
 				/* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
509 509
 				/* STATUS */ container.State.String(),
510
-				/* COMMENT */ comment,
510
+				/* COMMENT */ "",
511 511
 			} {
512 512
 				if idx == 0 {
513 513
 					w.Write([]byte(field))
... ...
@@ -540,17 +534,13 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
540 540
 	}
541 541
 	if container := srv.containers.Get(containerName); container != nil {
542 542
 		// FIXME: freeze the container before copying it to avoid data corruption?
543
-		rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed)
543
+		// FIXME: this shouldn't be in commands.
544
+		rwTar, err := container.ExportRw()
544 545
 		if err != nil {
545 546
 			return err
546 547
 		}
547 548
 		// Create a new image from the container's base layers + a new layer from container changes
548
-		parentImg, err := srv.images.Get(container.Image)
549
-		if err != nil {
550
-			return err
551
-		}
552
-
553
-		img, err := srv.images.Create(rwTar, parentImg, imgName, "")
549
+		img, err := srv.containers.graph.Create(rwTar, container.Image, "")
554 550
 		if err != nil {
555 551
 			return err
556 552
 		}
... ...
@@ -574,10 +564,10 @@ func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string)
574 574
 	}
575 575
 	name := cmd.Arg(0)
576 576
 	if container := srv.containers.Get(name); container != nil {
577
-		if err := container.Mountpoint.EnsureMounted(); err != nil {
577
+		if err := container.EnsureMounted(); err != nil {
578 578
 			return err
579 579
 		}
580
-		data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed)
580
+		data, err := container.Export()
581 581
 		if err != nil {
582 582
 			return err
583 583
 		}
... ...
@@ -603,7 +593,7 @@ func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string
603 603
 	if container := srv.containers.Get(cmd.Arg(0)); container == nil {
604 604
 		return errors.New("No such container")
605 605
 	} else {
606
-		changes, err := srv.images.Changes(container.Mountpoint)
606
+		changes, err := container.Changes()
607 607
 		if err != nil {
608 608
 			return err
609 609
 		}
... ...
@@ -625,10 +615,20 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
625 625
 	}
626 626
 	name := cmd.Arg(0)
627 627
 	if container := srv.containers.Get(name); container != nil {
628
-		if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
628
+		log_stdout, err := container.ReadLog("stdout")
629
+		if err != nil {
629 630
 			return err
630 631
 		}
631
-		if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
632
+		log_stderr, err := container.ReadLog("stderr")
633
+		if err != nil {
634
+			return err
635
+		}
636
+		// FIXME: Interpolate stdout and stderr instead of concatenating them
637
+		// FIXME: Differentiate stdout and stderr in the remote protocol
638
+		if _, err := io.Copy(stdout, log_stdout); err != nil {
639
+			return err
640
+		}
641
+		if _, err := io.Copy(stdout, log_stderr); err != nil {
632 642
 			return err
633 643
 		}
634 644
 		return nil
... ...
@@ -636,31 +636,6 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
636 636
 	return errors.New("No such container: " + cmd.Arg(0))
637 637
 }
638 638
 
639
-func (srv *Server) CreateContainer(img *fs.Image, ports []int, user string, tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*Container, error) {
640
-	id := future.RandomId()[:8]
641
-	container, err := srv.containers.Create(id, cmd, args, img,
642
-		&Config{
643
-			Hostname:  id,
644
-			Ports:     ports,
645
-			User:      user,
646
-			Tty:       tty,
647
-			OpenStdin: openStdin,
648
-			Memory:    memory,
649
-		})
650
-	if err != nil {
651
-		return nil, err
652
-	}
653
-	if err := container.SetUserData("image", img.Id); err != nil {
654
-		srv.containers.Destroy(container)
655
-		return nil, errors.New("Error setting container userdata: " + err.Error())
656
-	}
657
-	if err := container.SetUserData("comment", comment); err != nil {
658
-		srv.containers.Destroy(container)
659
-		return nil, errors.New("Error setting container userdata: " + err.Error())
660
-	}
661
-	return container, nil
662
-}
663
-
664 639
 func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
665 640
 	cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container")
666 641
 	fl_i := cmd.Bool("i", false, "Attach to stdin")
... ...
@@ -729,7 +704,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
729 729
 	fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
730 730
 	fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
731 731
 	fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
732
-	fl_comment := cmd.String("c", "", "Comment")
733 732
 	fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
734 733
 	var fl_ports ports
735 734
 
... ...
@@ -738,8 +712,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
738 738
 		return nil
739 739
 	}
740 740
 	name := cmd.Arg(0)
741
-	var img_name string
742
-	//var img_version string  // Only here for reference
743 741
 	var cmdline []string
744 742
 
745 743
 	if len(cmd.Args()) >= 2 {
... ...
@@ -758,33 +730,15 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
758 758
 		cmdline = []string{"/bin/bash", "-i"}
759 759
 	}
760 760
 
761
-	// Find the image
762
-	img, err := srv.images.Find(name)
763
-	if err != nil {
764
-		return err
765
-	} else if img == nil {
766
-		// Separate the name:version tag
767
-		if strings.Contains(name, ":") {
768
-			parts := strings.SplitN(name, ":", 2)
769
-			img_name = parts[0]
770
-			//img_version = parts[1]   // Only here for reference
771
-		} else {
772
-			img_name = name
773
-		}
774
-
775
-		stdin_noclose := ioutil.NopCloser(stdin)
776
-		if err := srv.CmdImport(stdin_noclose, stdout, img_name); err != nil {
777
-			return err
778
-		}
779
-		img, err = srv.images.Find(name)
780
-		if err != nil || img == nil {
781
-			return errors.New("Could not find image after downloading: " + name)
782
-		}
783
-	}
784
-
785 761
 	// Create new container
786
-	container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty,
787
-		*fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...)
762
+	container, err := srv.containers.Create(cmdline[0], cmdline[1:], name,
763
+		&Config{
764
+			Ports:     fl_ports,
765
+			User:      *fl_user,
766
+			Tty:       *fl_tty,
767
+			OpenStdin: *fl_stdin,
768
+			Memory:    *fl_memory,
769
+		})
788 770
 	if err != nil {
789 771
 		return errors.New("Error creating container: " + err.Error())
790 772
 	}
... ...
@@ -850,7 +804,6 @@ func NewServer() (*Server, error) {
850 850
 		return nil, err
851 851
 	}
852 852
 	srv := &Server{
853
-		images:     containers.Store,
854 853
 		containers: containers,
855 854
 	}
856 855
 	return srv, nil
... ...
@@ -858,5 +811,4 @@ func NewServer() (*Server, error) {
858 858
 
859 859
 type Server struct {
860 860
 	containers *Docker
861
-	images     *fs.Store
862 861
 }
... ...
@@ -3,7 +3,9 @@ package docker
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"errors"
6
-	"github.com/dotcloud/docker/fs"
6
+	"fmt"
7
+	"github.com/dotcloud/docker/future"
8
+	"github.com/dotcloud/docker/graph"
7 9
 	"github.com/kr/pty"
8 10
 	"io"
9 11
 	"io/ioutil"
... ...
@@ -16,40 +18,34 @@ import (
16 16
 	"time"
17 17
 )
18 18
 
19
-var sysInitPath string
20
-
21
-func init() {
22
-	sysInitPath = SelfPath()
23
-}
24
-
25 19
 type Container struct {
26
-	Id   string
27
-	Root string
20
+	root string
21
+
22
+	Id string
28 23
 
29 24
 	Created time.Time
30 25
 
31 26
 	Path string
32 27
 	Args []string
33 28
 
34
-	Config     *Config
35
-	Mountpoint *fs.Mountpoint
36
-	State      *State
37
-	Image      string
29
+	Config *Config
30
+	State  State
31
+	Image  string
38 32
 
39 33
 	network         *NetworkInterface
40 34
 	networkManager  *NetworkManager
41 35
 	NetworkSettings *NetworkSettings
42 36
 
43
-	SysInitPath   string
44
-	lxcConfigPath string
45
-	cmd           *exec.Cmd
46
-	stdout        *writeBroadcaster
47
-	stderr        *writeBroadcaster
48
-	stdin         io.ReadCloser
49
-	stdinPipe     io.WriteCloser
37
+	SysInitPath string
38
+	cmd         *exec.Cmd
39
+	stdout      *writeBroadcaster
40
+	stderr      *writeBroadcaster
41
+	stdin       io.ReadCloser
42
+	stdinPipe   io.WriteCloser
50 43
 
51 44
 	stdoutLog *os.File
52 45
 	stderrLog *os.File
46
+	runtime   *Docker // FIXME: rename Docker to Runtime for clarity
53 47
 }
54 48
 
55 49
 type Config struct {
... ...
@@ -69,104 +65,9 @@ type NetworkSettings struct {
69 69
 	PortMapping map[string]string
70 70
 }
71 71
 
72
-func createContainer(id string, root string, command string, args []string, image *fs.Image, config *Config, netManager *NetworkManager) (*Container, error) {
73
-	mountpoint, err := image.Mountpoint(path.Join(root, "rootfs"), path.Join(root, "rw"))
74
-	if err != nil {
75
-		return nil, err
76
-	}
77
-	container := &Container{
78
-		Id:              id,
79
-		Root:            root,
80
-		Created:         time.Now(),
81
-		Path:            command,
82
-		Args:            args,
83
-		Config:          config,
84
-		Image:           image.Id,
85
-		Mountpoint:      mountpoint,
86
-		State:           newState(),
87
-		networkManager:  netManager,
88
-		NetworkSettings: &NetworkSettings{},
89
-		SysInitPath:     sysInitPath,
90
-		lxcConfigPath:   path.Join(root, "config.lxc"),
91
-		stdout:          newWriteBroadcaster(),
92
-		stderr:          newWriteBroadcaster(),
93
-	}
94
-	if err := os.Mkdir(root, 0700); err != nil {
95
-		return nil, err
96
-	}
97
-	// Setup logging of stdout and stderr to disk
98
-	if stdoutLog, err := os.OpenFile(path.Join(container.Root, id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
99
-		return nil, err
100
-	} else {
101
-		container.stdoutLog = stdoutLog
102
-	}
103
-	if stderrLog, err := os.OpenFile(path.Join(container.Root, id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
104
-		return nil, err
105
-	} else {
106
-		container.stderrLog = stderrLog
107
-	}
108
-	if container.Config.OpenStdin {
109
-		container.stdin, container.stdinPipe = io.Pipe()
110
-	} else {
111
-		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
112
-	}
113
-	container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
114
-	container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
115
-
116
-	if err := container.save(); err != nil {
117
-		return nil, err
118
-	}
119
-	return container, nil
120
-}
121
-
122
-func loadContainer(store *fs.Store, containerPath string, netManager *NetworkManager) (*Container, error) {
123
-	data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
124
-	if err != nil {
125
-		return nil, err
126
-	}
127
-	mountpoint, err := store.FetchMountpoint(
128
-		path.Join(containerPath, "rootfs"),
129
-		path.Join(containerPath, "rw"),
130
-	)
131
-	if err != nil {
132
-		return nil, err
133
-	} else if mountpoint == nil {
134
-		return nil, errors.New("Couldn't load container: unregistered mountpoint.")
135
-	}
136
-	container := &Container{
137
-		stdout:          newWriteBroadcaster(),
138
-		stderr:          newWriteBroadcaster(),
139
-		lxcConfigPath:   path.Join(containerPath, "config.lxc"),
140
-		networkManager:  netManager,
141
-		NetworkSettings: &NetworkSettings{},
142
-		Mountpoint:      mountpoint,
143
-	}
144
-	// Load container settings
145
-	if err := json.Unmarshal(data, container); err != nil {
146
-		return nil, err
147
-	}
148
-
149
-	// Setup logging of stdout and stderr to disk
150
-	if stdoutLog, err := os.OpenFile(path.Join(container.Root, container.Id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
151
-		return nil, err
152
-	} else {
153
-		container.stdoutLog = stdoutLog
154
-	}
155
-	if stderrLog, err := os.OpenFile(path.Join(container.Root, container.Id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
156
-		return nil, err
157
-	} else {
158
-		container.stderrLog = stderrLog
159
-	}
160
-	container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
161
-	container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
162
-
163
-	if container.Config.OpenStdin {
164
-		container.stdin, container.stdinPipe = io.Pipe()
165
-	} else {
166
-		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
167
-	}
168
-	container.State = newState()
169
-	return container, nil
72
+func GenerateId() string {
73
+	future.Seed()
74
+	return future.RandomId()
170 75
 }
171 76
 
172 77
 func (container *Container) Cmd() *exec.Cmd {
... ...
@@ -177,64 +78,32 @@ func (container *Container) When() time.Time {
177 177
 	return container.Created
178 178
 }
179 179
 
180
-func (container *Container) loadUserData() (map[string]string, error) {
181
-	jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
182
-	if err != nil {
183
-		if os.IsNotExist(err) {
184
-			return make(map[string]string), nil
185
-		}
186
-		return nil, err
187
-	}
188
-	data := make(map[string]string)
189
-	if err := json.Unmarshal(jsonData, &data); err != nil {
190
-		return nil, err
191
-	}
192
-	return data, nil
193
-}
194
-
195
-func (container *Container) saveUserData(data map[string]string) error {
196
-	jsonData, err := json.Marshal(data)
180
+func (container *Container) FromDisk() error {
181
+	data, err := ioutil.ReadFile(container.jsonPath())
197 182
 	if err != nil {
198 183
 		return err
199 184
 	}
200
-	return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700)
201
-}
202
-
203
-func (container *Container) SetUserData(key, value string) error {
204
-	data, err := container.loadUserData()
205
-	if err != nil {
185
+	// Load container settings
186
+	if err := json.Unmarshal(data, container); err != nil {
206 187
 		return err
207 188
 	}
208
-	data[key] = value
209
-	return container.saveUserData(data)
210
-}
211
-
212
-func (container *Container) GetUserData(key string) string {
213
-	data, err := container.loadUserData()
214
-	if err != nil {
215
-		return ""
216
-	}
217
-	if value, exists := data[key]; exists {
218
-		return value
219
-	}
220
-	return ""
189
+	return nil
221 190
 }
222 191
 
223
-func (container *Container) save() (err error) {
192
+func (container *Container) ToDisk() (err error) {
224 193
 	data, err := json.Marshal(container)
225 194
 	if err != nil {
226 195
 		return
227 196
 	}
228
-	return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0666)
197
+	return ioutil.WriteFile(container.jsonPath(), data, 0666)
229 198
 }
230 199
 
231 200
 func (container *Container) generateLXCConfig() error {
232
-	fo, err := os.Create(container.lxcConfigPath)
201
+	fo, err := os.Create(container.lxcConfigPath())
233 202
 	if err != nil {
234 203
 		return err
235 204
 	}
236 205
 	defer fo.Close()
237
-
238 206
 	if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
239 207
 		return err
240 208
 	}
... ...
@@ -309,7 +178,7 @@ func (container *Container) start() error {
309 309
 }
310 310
 
311 311
 func (container *Container) Start() error {
312
-	if err := container.Mountpoint.EnsureMounted(); err != nil {
312
+	if err := container.EnsureMounted(); err != nil {
313 313
 		return err
314 314
 	}
315 315
 	if err := container.allocateNetwork(); err != nil {
... ...
@@ -320,7 +189,7 @@ func (container *Container) Start() error {
320 320
 	}
321 321
 	params := []string{
322 322
 		"-n", container.Id,
323
-		"-f", container.lxcConfigPath,
323
+		"-f", container.lxcConfigPath(),
324 324
 		"--",
325 325
 		"/sbin/init",
326 326
 	}
... ...
@@ -348,8 +217,10 @@ func (container *Container) Start() error {
348 348
 	if err != nil {
349 349
 		return err
350 350
 	}
351
+	// FIXME: save state on disk *first*, then converge
352
+	// this way disk state is used as a journal, eg. we can restore after crash etc.
351 353
 	container.State.setRunning(container.cmd.Process.Pid)
352
-	container.save()
354
+	container.ToDisk()
353 355
 	go container.monitor()
354 356
 	return nil
355 357
 }
... ...
@@ -389,28 +260,12 @@ func (container *Container) StdoutPipe() (io.ReadCloser, error) {
389 389
 	return newBufReader(reader), nil
390 390
 }
391 391
 
392
-func (container *Container) StdoutLog() io.Reader {
393
-	r, err := os.Open(container.stdoutLog.Name())
394
-	if err != nil {
395
-		return nil
396
-	}
397
-	return r
398
-}
399
-
400 392
 func (container *Container) StderrPipe() (io.ReadCloser, error) {
401 393
 	reader, writer := io.Pipe()
402 394
 	container.stderr.AddWriter(writer)
403 395
 	return newBufReader(reader), nil
404 396
 }
405 397
 
406
-func (container *Container) StderrLog() io.Reader {
407
-	r, err := os.Open(container.stderrLog.Name())
408
-	if err != nil {
409
-		return nil
410
-	}
411
-	return r
412
-}
413
-
414 398
 func (container *Container) allocateNetwork() error {
415 399
 	iface, err := container.networkManager.Allocate()
416 400
 	if err != nil {
... ...
@@ -450,7 +305,7 @@ func (container *Container) monitor() {
450 450
 	}
451 451
 	container.stdout.Close()
452 452
 	container.stderr.Close()
453
-	if err := container.Mountpoint.Umount(); err != nil {
453
+	if err := container.Unmount(); err != nil {
454 454
 		log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
455 455
 	}
456 456
 
... ...
@@ -461,7 +316,7 @@ func (container *Container) monitor() {
461 461
 
462 462
 	// Report status back
463 463
 	container.State.setStopped(exitCode)
464
-	container.save()
464
+	container.ToDisk()
465 465
 }
466 466
 
467 467
 func (container *Container) kill() error {
... ...
@@ -523,6 +378,17 @@ func (container *Container) Wait() int {
523 523
 	return container.State.ExitCode
524 524
 }
525 525
 
526
+func (container *Container) ExportRw() (graph.Archive, error) {
527
+	return graph.Tar(container.rwPath(), graph.Uncompressed)
528
+}
529
+
530
+func (container *Container) Export() (graph.Archive, error) {
531
+	if err := container.EnsureMounted(); err != nil {
532
+		return nil, err
533
+	}
534
+	return graph.Tar(container.RootfsPath(), graph.Uncompressed)
535
+}
536
+
526 537
 func (container *Container) WaitTimeout(timeout time.Duration) error {
527 538
 	done := make(chan bool)
528 539
 	go func() {
... ...
@@ -538,3 +404,75 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
538 538
 	}
539 539
 	return nil
540 540
 }
541
+
542
+func (container *Container) EnsureMounted() error {
543
+	if mounted, err := container.Mounted(); err != nil {
544
+		return err
545
+	} else if mounted {
546
+		return nil
547
+	}
548
+	return container.Mount()
549
+}
550
+
551
+func (container *Container) Mount() error {
552
+	image, err := container.GetImage()
553
+	if err != nil {
554
+		return err
555
+	}
556
+	return image.Mount(container.RootfsPath(), container.rwPath())
557
+}
558
+
559
+func (container *Container) Changes() ([]graph.Change, error) {
560
+	image, err := container.GetImage()
561
+	if err != nil {
562
+		return nil, err
563
+	}
564
+	return image.Changes(container.rwPath())
565
+}
566
+
567
+func (container *Container) GetImage() (*graph.Image, error) {
568
+	if container.runtime == nil {
569
+		return nil, fmt.Errorf("Can't get image of unregistered container")
570
+	}
571
+	return container.runtime.graph.Get(container.Image)
572
+}
573
+
574
+func (container *Container) Mounted() (bool, error) {
575
+	return graph.Mounted(container.RootfsPath())
576
+}
577
+
578
+func (container *Container) Unmount() error {
579
+	return graph.Unmount(container.RootfsPath())
580
+}
581
+
582
+func (container *Container) logPath(name string) string {
583
+	return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
584
+}
585
+
586
+func (container *Container) ReadLog(name string) (io.Reader, error) {
587
+	return os.Open(container.logPath(name))
588
+}
589
+
590
+func (container *Container) jsonPath() string {
591
+	return path.Join(container.root, "config.json")
592
+}
593
+
594
+func (container *Container) lxcConfigPath() string {
595
+	return path.Join(container.root, "config.lxc")
596
+}
597
+
598
+// This method must be exported to be used from the lxc template
599
+func (container *Container) RootfsPath() string {
600
+	return path.Join(container.root, "rootfs")
601
+}
602
+
603
+func (container *Container) rwPath() string {
604
+	return path.Join(container.root, "rw")
605
+}
606
+
607
+func validateId(id string) error {
608
+	if id == "" {
609
+		return fmt.Errorf("Invalid empty id")
610
+	}
611
+	return nil
612
+}
... ...
@@ -3,7 +3,6 @@ package docker
3 3
 import (
4 4
 	"bufio"
5 5
 	"fmt"
6
-	"github.com/dotcloud/docker/fs"
7 6
 	"io"
8 7
 	"io/ioutil"
9 8
 	"math/rand"
... ...
@@ -21,10 +20,9 @@ func TestCommitRun(t *testing.T) {
21 21
 	}
22 22
 	defer nuke(docker)
23 23
 	container1, err := docker.Create(
24
-		"precommit_test",
25 24
 		"/bin/sh",
26 25
 		[]string{"-c", "echo hello > /world"},
27
-		GetTestImage(docker),
26
+		GetTestImage(docker).Id,
28 27
 		&Config{
29 28
 			Memory: 33554432,
30 29
 		},
... ...
@@ -44,18 +42,11 @@ func TestCommitRun(t *testing.T) {
44 44
 		t.Errorf("Container shouldn't be running")
45 45
 	}
46 46
 
47
-	// FIXME: freeze the container before copying it to avoid data corruption?
48
-	rwTar, err := fs.Tar(container1.Mountpoint.Rw, fs.Uncompressed)
47
+	rwTar, err := container1.ExportRw()
49 48
 	if err != nil {
50 49
 		t.Error(err)
51 50
 	}
52
-	// Create a new image from the container's base layers + a new layer from container changes
53
-	parentImg, err := docker.Store.Get(container1.Image)
54
-	if err != nil {
55
-		t.Error(err)
56
-	}
57
-
58
-	img, err := docker.Store.Create(rwTar, parentImg, "test_commitrun", "unit test commited image")
51
+	img, err := docker.graph.Create(rwTar, container1.Image, "unit test commited image")
59 52
 	if err != nil {
60 53
 		t.Error(err)
61 54
 	}
... ...
@@ -63,10 +54,9 @@ func TestCommitRun(t *testing.T) {
63 63
 	// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
64 64
 
65 65
 	container2, err := docker.Create(
66
-		"postcommit_test",
67 66
 		"cat",
68 67
 		[]string{"/world"},
69
-		img,
68
+		img.Id,
70 69
 		&Config{
71 70
 			Memory: 33554432,
72 71
 		},
... ...
@@ -98,10 +88,9 @@ func TestRun(t *testing.T) {
98 98
 	}
99 99
 	defer nuke(docker)
100 100
 	container, err := docker.Create(
101
-		"run_test",
102 101
 		"ls",
103 102
 		[]string{"-al"},
104
-		GetTestImage(docker),
103
+		GetTestImage(docker).Id,
105 104
 		&Config{
106 105
 			Memory: 33554432,
107 106
 		},
... ...
@@ -129,10 +118,9 @@ func TestOutput(t *testing.T) {
129 129
 	}
130 130
 	defer nuke(docker)
131 131
 	container, err := docker.Create(
132
-		"output_test",
133 132
 		"echo",
134 133
 		[]string{"-n", "foobar"},
135
-		GetTestImage(docker),
134
+		GetTestImage(docker).Id,
136 135
 		&Config{},
137 136
 	)
138 137
 	if err != nil {
... ...
@@ -155,10 +143,9 @@ func TestKill(t *testing.T) {
155 155
 	}
156 156
 	defer nuke(docker)
157 157
 	container, err := docker.Create(
158
-		"stop_test",
159 158
 		"cat",
160 159
 		[]string{"/dev/zero"},
161
-		GetTestImage(docker),
160
+		GetTestImage(docker).Id,
162 161
 		&Config{},
163 162
 	)
164 163
 	if err != nil {
... ...
@@ -199,10 +186,9 @@ func TestExitCode(t *testing.T) {
199 199
 	defer nuke(docker)
200 200
 
201 201
 	trueContainer, err := docker.Create(
202
-		"exit_test_1",
203 202
 		"/bin/true",
204 203
 		[]string{""},
205
-		GetTestImage(docker),
204
+		GetTestImage(docker).Id,
206 205
 		&Config{},
207 206
 	)
208 207
 	if err != nil {
... ...
@@ -214,10 +200,9 @@ func TestExitCode(t *testing.T) {
214 214
 	}
215 215
 
216 216
 	falseContainer, err := docker.Create(
217
-		"exit_test_2",
218 217
 		"/bin/false",
219 218
 		[]string{""},
220
-		GetTestImage(docker),
219
+		GetTestImage(docker).Id,
221 220
 		&Config{},
222 221
 	)
223 222
 	if err != nil {
... ...
@@ -244,10 +229,9 @@ func TestRestart(t *testing.T) {
244 244
 	}
245 245
 	defer nuke(docker)
246 246
 	container, err := docker.Create(
247
-		"restart_test",
248 247
 		"echo",
249 248
 		[]string{"-n", "foobar"},
250
-		GetTestImage(docker),
249
+		GetTestImage(docker).Id,
251 250
 		&Config{},
252 251
 	)
253 252
 	if err != nil {
... ...
@@ -279,10 +263,9 @@ func TestRestartStdin(t *testing.T) {
279 279
 	}
280 280
 	defer nuke(docker)
281 281
 	container, err := docker.Create(
282
-		"restart_stdin_test",
283 282
 		"cat",
284 283
 		[]string{},
285
-		GetTestImage(docker),
284
+		GetTestImage(docker).Id,
286 285
 		&Config{
287 286
 			OpenStdin: true,
288 287
 		},
... ...
@@ -331,10 +314,9 @@ func TestUser(t *testing.T) {
331 331
 
332 332
 	// Default user must be root
333 333
 	container, err := docker.Create(
334
-		"user_default",
335 334
 		"id",
336 335
 		[]string{},
337
-		GetTestImage(docker),
336
+		GetTestImage(docker).Id,
338 337
 		&Config{},
339 338
 	)
340 339
 	if err != nil {
... ...
@@ -351,10 +333,9 @@ func TestUser(t *testing.T) {
351 351
 
352 352
 	// Set a username
353 353
 	container, err = docker.Create(
354
-		"user_root",
355 354
 		"id",
356 355
 		[]string{},
357
-		GetTestImage(docker),
356
+		GetTestImage(docker).Id,
358 357
 		&Config{
359 358
 			User: "root",
360 359
 		},
... ...
@@ -373,10 +354,9 @@ func TestUser(t *testing.T) {
373 373
 
374 374
 	// Set a UID
375 375
 	container, err = docker.Create(
376
-		"user_uid0",
377 376
 		"id",
378 377
 		[]string{},
379
-		GetTestImage(docker),
378
+		GetTestImage(docker).Id,
380 379
 		&Config{
381 380
 			User: "0",
382 381
 		},
... ...
@@ -395,10 +375,9 @@ func TestUser(t *testing.T) {
395 395
 
396 396
 	// Set a different user by uid
397 397
 	container, err = docker.Create(
398
-		"user_uid1",
399 398
 		"id",
400 399
 		[]string{},
401
-		GetTestImage(docker),
400
+		GetTestImage(docker).Id,
402 401
 		&Config{
403 402
 			User: "1",
404 403
 		},
... ...
@@ -419,10 +398,9 @@ func TestUser(t *testing.T) {
419 419
 
420 420
 	// Set a different user by username
421 421
 	container, err = docker.Create(
422
-		"user_daemon",
423 422
 		"id",
424 423
 		[]string{},
425
-		GetTestImage(docker),
424
+		GetTestImage(docker).Id,
426 425
 		&Config{
427 426
 			User: "daemon",
428 427
 		},
... ...
@@ -448,10 +426,9 @@ func TestMultipleContainers(t *testing.T) {
448 448
 	defer nuke(docker)
449 449
 
450 450
 	container1, err := docker.Create(
451
-		"container1",
452 451
 		"cat",
453 452
 		[]string{"/dev/zero"},
454
-		GetTestImage(docker),
453
+		GetTestImage(docker).Id,
455 454
 		&Config{},
456 455
 	)
457 456
 	if err != nil {
... ...
@@ -460,10 +437,9 @@ func TestMultipleContainers(t *testing.T) {
460 460
 	defer docker.Destroy(container1)
461 461
 
462 462
 	container2, err := docker.Create(
463
-		"container2",
464 463
 		"cat",
465 464
 		[]string{"/dev/zero"},
466
-		GetTestImage(docker),
465
+		GetTestImage(docker).Id,
467 466
 		&Config{},
468 467
 	)
469 468
 	if err != nil {
... ...
@@ -504,10 +480,9 @@ func TestStdin(t *testing.T) {
504 504
 	}
505 505
 	defer nuke(docker)
506 506
 	container, err := docker.Create(
507
-		"stdin_test",
508 507
 		"cat",
509 508
 		[]string{},
510
-		GetTestImage(docker),
509
+		GetTestImage(docker).Id,
511 510
 		&Config{
512 511
 			OpenStdin: true,
513 512
 		},
... ...
@@ -540,10 +515,9 @@ func TestTty(t *testing.T) {
540 540
 	}
541 541
 	defer nuke(docker)
542 542
 	container, err := docker.Create(
543
-		"tty_test",
544 543
 		"cat",
545 544
 		[]string{},
546
-		GetTestImage(docker),
545
+		GetTestImage(docker).Id,
547 546
 		&Config{
548 547
 			OpenStdin: true,
549 548
 		},
... ...
@@ -576,10 +550,9 @@ func TestEnv(t *testing.T) {
576 576
 	}
577 577
 	defer nuke(docker)
578 578
 	container, err := docker.Create(
579
-		"env_test",
580 579
 		"/usr/bin/env",
581 580
 		[]string{},
582
-		GetTestImage(docker),
581
+		GetTestImage(docker).Id,
583 582
 		&Config{},
584 583
 	)
585 584
 	if err != nil {
... ...
@@ -651,10 +624,9 @@ func TestLXCConfig(t *testing.T) {
651 651
 	memMax := 536870912
652 652
 	mem := memMin + rand.Intn(memMax-memMin)
653 653
 	container, err := docker.Create(
654
-		"config_test",
655 654
 		"/bin/true",
656 655
 		[]string{},
657
-		GetTestImage(docker),
656
+		GetTestImage(docker).Id,
658 657
 		&Config{
659 658
 			Hostname: "foobar",
660 659
 			Memory:   int64(mem),
... ...
@@ -665,10 +637,10 @@ func TestLXCConfig(t *testing.T) {
665 665
 	}
666 666
 	defer docker.Destroy(container)
667 667
 	container.generateLXCConfig()
668
-	grepFile(t, container.lxcConfigPath, "lxc.utsname = foobar")
669
-	grepFile(t, container.lxcConfigPath,
668
+	grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar")
669
+	grepFile(t, container.lxcConfigPath(),
670 670
 		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
671
-	grepFile(t, container.lxcConfigPath,
671
+	grepFile(t, container.lxcConfigPath(),
672 672
 		fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
673 673
 }
674 674
 
... ...
@@ -680,10 +652,9 @@ func BenchmarkRunSequencial(b *testing.B) {
680 680
 	defer nuke(docker)
681 681
 	for i := 0; i < b.N; i++ {
682 682
 		container, err := docker.Create(
683
-			fmt.Sprintf("bench_%v", i),
684 683
 			"echo",
685 684
 			[]string{"-n", "foo"},
686
-			GetTestImage(docker),
685
+			GetTestImage(docker).Id,
687 686
 			&Config{},
688 687
 		)
689 688
 		if err != nil {
... ...
@@ -717,10 +688,9 @@ func BenchmarkRunParallel(b *testing.B) {
717 717
 		tasks = append(tasks, complete)
718 718
 		go func(i int, complete chan error) {
719 719
 			container, err := docker.Create(
720
-				fmt.Sprintf("bench_%v", i),
721 720
 				"echo",
722 721
 				[]string{"-n", "foo"},
723
-				GetTestImage(docker),
722
+				GetTestImage(docker).Id,
724 723
 				&Config{},
725 724
 			)
726 725
 			if err != nil {
... ...
@@ -3,12 +3,15 @@ package docker
3 3
 import (
4 4
 	"container/list"
5 5
 	"fmt"
6
-	"github.com/dotcloud/docker/fs"
6
+	"github.com/dotcloud/docker/graph"
7
+	"io"
7 8
 	"io/ioutil"
8 9
 	"log"
9 10
 	"os"
10 11
 	"path"
11 12
 	"sort"
13
+	"sync"
14
+	"time"
12 15
 )
13 16
 
14 17
 type Docker struct {
... ...
@@ -16,7 +19,13 @@ type Docker struct {
16 16
 	repository     string
17 17
 	containers     *list.List
18 18
 	networkManager *NetworkManager
19
-	Store          *fs.Store
19
+	graph          *graph.Graph
20
+}
21
+
22
+var sysInitPath string
23
+
24
+func init() {
25
+	sysInitPath = SelfPath()
20 26
 }
21 27
 
22 28
 func (docker *Docker) List() []*Container {
... ...
@@ -49,20 +58,98 @@ func (docker *Docker) Exists(id string) bool {
49 49
 	return docker.Get(id) != nil
50 50
 }
51 51
 
52
-func (docker *Docker) Create(id string, command string, args []string, image *fs.Image, config *Config) (*Container, error) {
53
-	if docker.Exists(id) {
54
-		return nil, fmt.Errorf("Container %v already exists", id)
52
+func (docker *Docker) containerRoot(id string) string {
53
+	return path.Join(docker.repository, id)
54
+}
55
+
56
+func (docker *Docker) Create(command string, args []string, image string, config *Config) (*Container, error) {
57
+	container := &Container{
58
+		// FIXME: we should generate the ID here instead of receiving it as an argument
59
+		Id:              GenerateId(),
60
+		Created:         time.Now(),
61
+		Path:            command,
62
+		Args:            args,
63
+		Config:          config,
64
+		Image:           image,
65
+		NetworkSettings: &NetworkSettings{},
66
+		// FIXME: do we need to store this in the container?
67
+		SysInitPath: sysInitPath,
68
+	}
69
+	container.root = docker.containerRoot(container.Id)
70
+	// Step 1: create the container directory.
71
+	// This doubles as a barrier to avoid race conditions.
72
+	if err := os.Mkdir(container.root, 0700); err != nil {
73
+		return nil, err
74
+	}
75
+	// Step 2: save the container json
76
+	if err := container.ToDisk(); err != nil {
77
+		return nil, err
78
+	}
79
+	// Step 3: register the container
80
+	if err := docker.Register(container); err != nil {
81
+		return nil, err
55 82
 	}
56
-	root := path.Join(docker.repository, id)
83
+	return container, nil
84
+}
57 85
 
58
-	container, err := createContainer(id, root, command, args, image, config, docker.networkManager)
59
-	if err != nil {
86
+func (docker *Docker) Load(id string) (*Container, error) {
87
+	container := &Container{root: docker.containerRoot(id)}
88
+	if err := container.FromDisk(); err != nil {
89
+		return nil, err
90
+	}
91
+	if container.Id != id {
92
+		return container, fmt.Errorf("Container %s is stored at %s", container.Id, id)
93
+	}
94
+	if err := docker.Register(container); err != nil {
60 95
 		return nil, err
61 96
 	}
62
-	docker.containers.PushBack(container)
63 97
 	return container, nil
64 98
 }
65 99
 
100
+// Register makes a container object usable by the runtime as <container.Id>
101
+func (docker *Docker) Register(container *Container) error {
102
+	if container.runtime != nil || docker.Exists(container.Id) {
103
+		return fmt.Errorf("Container is already loaded")
104
+	}
105
+	if err := validateId(container.Id); err != nil {
106
+		return err
107
+	}
108
+	container.runtime = docker
109
+	container.networkManager = docker.networkManager // FIXME: infer from docker.runtime
110
+	// Setup state lock (formerly in newState()
111
+	lock := new(sync.Mutex)
112
+	container.State.stateChangeLock = lock
113
+	container.State.stateChangeCond = sync.NewCond(lock)
114
+	// Attach to stdout and stderr
115
+	container.stderr = newWriteBroadcaster()
116
+	container.stdout = newWriteBroadcaster()
117
+	// Attach to stdin
118
+	if container.Config.OpenStdin {
119
+		container.stdin, container.stdinPipe = io.Pipe()
120
+	} else {
121
+		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
122
+	}
123
+	// Setup logging of stdout and stderr to disk
124
+	if err := docker.LogToDisk(container.stdout, container.logPath("stdout")); err != nil {
125
+		return err
126
+	}
127
+	if err := docker.LogToDisk(container.stderr, container.logPath("stderr")); err != nil {
128
+		return err
129
+	}
130
+	// done
131
+	docker.containers.PushBack(container)
132
+	return nil
133
+}
134
+
135
+func (docker *Docker) LogToDisk(src *writeBroadcaster, dst string) error {
136
+	log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
137
+	if err != nil {
138
+		return err
139
+	}
140
+	src.AddWriter(NopWriteCloser(log))
141
+	return nil
142
+}
143
+
66 144
 func (docker *Docker) Destroy(container *Container) error {
67 145
 	element := docker.getContainerElement(container.Id)
68 146
 	if element == nil {
... ...
@@ -72,18 +159,18 @@ func (docker *Docker) Destroy(container *Container) error {
72 72
 	if err := container.Stop(); err != nil {
73 73
 		return err
74 74
 	}
75
-	if container.Mountpoint.Mounted() {
76
-		if err := container.Mountpoint.Umount(); err != nil {
77
-			return fmt.Errorf("Unable to umount container %v: %v", container.Id, err)
75
+	if mounted, err := container.Mounted(); err != nil {
76
+		return err
77
+	} else if mounted {
78
+		if err := container.Unmount(); err != nil {
79
+			return fmt.Errorf("Unable to unmount container %v: %v", container.Id, err)
78 80
 		}
79 81
 	}
80
-	if err := container.Mountpoint.Deregister(); err != nil {
81
-		return fmt.Errorf("Unable to deregiser -- ? mountpoint %v: %v", container.Mountpoint.Root, err)
82
-	}
83
-	if err := os.RemoveAll(container.Root); err != nil {
82
+	// Deregister the container before removing its directory, to avoid race conditions
83
+	docker.containers.Remove(element)
84
+	if err := os.RemoveAll(container.root); err != nil {
84 85
 		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err)
85 86
 	}
86
-	docker.containers.Remove(element)
87 87
 	return nil
88 88
 }
89 89
 
... ...
@@ -93,12 +180,13 @@ func (docker *Docker) restore() error {
93 93
 		return err
94 94
 	}
95 95
 	for _, v := range dir {
96
-		container, err := loadContainer(docker.Store, path.Join(docker.repository, v.Name()), docker.networkManager)
96
+		id := v.Name()
97
+		container, err := docker.Load(id)
97 98
 		if err != nil {
98
-			log.Printf("Failed to load container %v: %v", v.Name(), err)
99
+			log.Printf("Failed to load container %v: %v", id, err)
99 100
 			continue
100 101
 		}
101
-		docker.containers.PushBack(container)
102
+		log.Printf("Loaded container %v", container.Id)
102 103
 	}
103 104
 	return nil
104 105
 }
... ...
@@ -114,7 +202,7 @@ func NewFromDirectory(root string) (*Docker, error) {
114 114
 		return nil, err
115 115
 	}
116 116
 
117
-	store, err := fs.New(path.Join(root, "images"))
117
+	graph, err := graph.New(path.Join(root, "graph"))
118 118
 	if err != nil {
119 119
 		return nil, err
120 120
 	}
... ...
@@ -127,8 +215,8 @@ func NewFromDirectory(root string) (*Docker, error) {
127 127
 		root:           root,
128 128
 		repository:     docker_repo,
129 129
 		containers:     list.New(),
130
-		Store:          store,
131 130
 		networkManager: netManager,
131
+		graph:          graph,
132 132
 	}
133 133
 
134 134
 	if err := docker.restore(); err != nil {
... ...
@@ -1,7 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"github.com/dotcloud/docker/fs"
4
+	"github.com/dotcloud/docker/graph"
5 5
 	"io"
6 6
 	"io/ioutil"
7 7
 	"os"
... ...
@@ -63,7 +63,6 @@ func init() {
63 63
 	}
64 64
 	// Create the "Server"
65 65
 	srv := &Server{
66
-		images:     docker.Store,
67 66
 		containers: docker,
68 67
 	}
69 68
 	// Retrieve the Image
... ...
@@ -93,8 +92,8 @@ func newTestDocker() (*Docker, error) {
93 93
 	return docker, nil
94 94
 }
95 95
 
96
-func GetTestImage(docker *Docker) *fs.Image {
97
-	imgs, err := docker.Store.Images()
96
+func GetTestImage(docker *Docker) *graph.Image {
97
+	imgs, err := docker.graph.All()
98 98
 	if err != nil {
99 99
 		panic(err)
100 100
 	} else if len(imgs) < 1 {
... ...
@@ -115,10 +114,9 @@ func TestCreate(t *testing.T) {
115 115
 		t.Errorf("Expected 0 containers, %v found", len(docker.List()))
116 116
 	}
117 117
 	container, err := docker.Create(
118
-		"test_create",
119 118
 		"ls",
120 119
 		[]string{"-al"},
121
-		GetTestImage(docker),
120
+		GetTestImage(docker).Id,
122 121
 		&Config{},
123 122
 	)
124 123
 	if err != nil {
... ...
@@ -137,22 +135,22 @@ func TestCreate(t *testing.T) {
137 137
 	}
138 138
 
139 139
 	// Make sure the container List() returns is the right one
140
-	if docker.List()[0].Id != "test_create" {
140
+	if docker.List()[0].Id != container.Id {
141 141
 		t.Errorf("Unexpected container %v returned by List", docker.List()[0])
142 142
 	}
143 143
 
144 144
 	// Make sure we can get the container with Get()
145
-	if docker.Get("test_create") == nil {
145
+	if docker.Get(container.Id) == nil {
146 146
 		t.Errorf("Unable to get newly created container")
147 147
 	}
148 148
 
149 149
 	// Make sure it is the right container
150
-	if docker.Get("test_create") != container {
150
+	if docker.Get(container.Id) != container {
151 151
 		t.Errorf("Get() returned the wrong container")
152 152
 	}
153 153
 
154 154
 	// Make sure Exists returns it as existing
155
-	if !docker.Exists("test_create") {
155
+	if !docker.Exists(container.Id) {
156 156
 		t.Errorf("Exists() returned false for a newly created container")
157 157
 	}
158 158
 }
... ...
@@ -164,10 +162,9 @@ func TestDestroy(t *testing.T) {
164 164
 	}
165 165
 	defer nuke(docker)
166 166
 	container, err := docker.Create(
167
-		"test_destroy",
168 167
 		"ls",
169 168
 		[]string{"-al"},
170
-		GetTestImage(docker),
169
+		GetTestImage(docker).Id,
171 170
 		&Config{},
172 171
 	)
173 172
 	if err != nil {
... ...
@@ -189,12 +186,12 @@ func TestDestroy(t *testing.T) {
189 189
 	}
190 190
 
191 191
 	// Make sure docker.Get() refuses to return the unexisting container
192
-	if docker.Get("test_destroy") != nil {
192
+	if docker.Get(container.Id) != nil {
193 193
 		t.Errorf("Unable to get newly created container")
194 194
 	}
195 195
 
196 196
 	// Make sure the container root directory does not exist anymore
197
-	_, err = os.Stat(container.Root)
197
+	_, err = os.Stat(container.root)
198 198
 	if err == nil || !os.IsNotExist(err) {
199 199
 		t.Errorf("Container root directory still exists after destroy")
200 200
 	}
... ...
@@ -213,10 +210,9 @@ func TestGet(t *testing.T) {
213 213
 	}
214 214
 	defer nuke(docker)
215 215
 	container1, err := docker.Create(
216
-		"test1",
217 216
 		"ls",
218 217
 		[]string{"-al"},
219
-		GetTestImage(docker),
218
+		GetTestImage(docker).Id,
220 219
 		&Config{},
221 220
 	)
222 221
 	if err != nil {
... ...
@@ -225,10 +221,9 @@ func TestGet(t *testing.T) {
225 225
 	defer docker.Destroy(container1)
226 226
 
227 227
 	container2, err := docker.Create(
228
-		"test2",
229 228
 		"ls",
230 229
 		[]string{"-al"},
231
-		GetTestImage(docker),
230
+		GetTestImage(docker).Id,
232 231
 		&Config{},
233 232
 	)
234 233
 	if err != nil {
... ...
@@ -237,10 +232,9 @@ func TestGet(t *testing.T) {
237 237
 	defer docker.Destroy(container2)
238 238
 
239 239
 	container3, err := docker.Create(
240
-		"test3",
241 240
 		"ls",
242 241
 		[]string{"-al"},
243
-		GetTestImage(docker),
242
+		GetTestImage(docker).Id,
244 243
 		&Config{},
245 244
 	)
246 245
 	if err != nil {
... ...
@@ -248,16 +242,16 @@ func TestGet(t *testing.T) {
248 248
 	}
249 249
 	defer docker.Destroy(container3)
250 250
 
251
-	if docker.Get("test1") != container1 {
252
-		t.Errorf("Get(test1) returned %v while expecting %v", docker.Get("test1"), container1)
251
+	if docker.Get(container1.Id) != container1 {
252
+		t.Errorf("Get(test1) returned %v while expecting %v", docker.Get(container1.Id), container1)
253 253
 	}
254 254
 
255
-	if docker.Get("test2") != container2 {
256
-		t.Errorf("Get(test2) returned %v while expecting %v", docker.Get("test2"), container2)
255
+	if docker.Get(container2.Id) != container2 {
256
+		t.Errorf("Get(test2) returned %v while expecting %v", docker.Get(container2.Id), container2)
257 257
 	}
258 258
 
259
-	if docker.Get("test3") != container3 {
260
-		t.Errorf("Get(test3) returned %v while expecting %v", docker.Get("test3"), container3)
259
+	if docker.Get(container3.Id) != container3 {
260
+		t.Errorf("Get(test3) returned %v while expecting %v", docker.Get(container3.Id), container3)
261 261
 	}
262 262
 
263 263
 }
... ...
@@ -282,10 +276,9 @@ func TestRestore(t *testing.T) {
282 282
 
283 283
 	// Create a container with one instance of docker
284 284
 	container1, err := docker1.Create(
285
-		"restore_test",
286 285
 		"ls",
287 286
 		[]string{"-al"},
288
-		GetTestImage(docker1),
287
+		GetTestImage(docker1).Id,
289 288
 		&Config{},
290 289
 	)
291 290
 	if err != nil {
... ...
@@ -309,7 +302,7 @@ func TestRestore(t *testing.T) {
309 309
 	if len(docker2.List()) != 1 {
310 310
 		t.Errorf("Expected 1 container, %v found", len(docker2.List()))
311 311
 	}
312
-	container2 := docker2.Get("restore_test")
312
+	container2 := docker2.Get(container1.Id)
313 313
 	if container2 == nil {
314 314
 		t.Fatal("Unable to Get container")
315 315
 	}
... ...
@@ -22,7 +22,7 @@ lxc.network.mtu = 1500
22 22
 lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}}
23 23
 
24 24
 # root filesystem
25
-{{$ROOTFS := .Mountpoint.Root}}
25
+{{$ROOTFS := .RootfsPath}}
26 26
 lxc.rootfs = {{$ROOTFS}}
27 27
 
28 28
 # use a dedicated pts for the container (and limit the number of pseudo terminal
... ...
@@ -17,14 +17,6 @@ type State struct {
17 17
 	stateChangeCond *sync.Cond
18 18
 }
19 19
 
20
-func newState() *State {
21
-	lock := new(sync.Mutex)
22
-	return &State{
23
-		stateChangeLock: lock,
24
-		stateChangeCond: sync.NewCond(lock),
25
-	}
26
-}
27
-
28 20
 // String returns a human-readable description of the state
29 21
 func (s *State) String() string {
30 22
 	if s.Running {