瀏覽代碼

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

Solomon Hykes 12 年之前
父節點
當前提交
7c57a4cfc0
共有 7 個文件被更改,包括 362 次插入429 次删除
  1. 85 133
      commands.go
  2. 117 179
      container.go
  3. 27 57
      container_test.go
  4. 110 22
      docker.go
  5. 22 29
      docker_test.go
  6. 1 1
      lxc_template.go
  7. 0 8
      state.go

+ 85 - 133
commands.go

@@ -1,20 +1,16 @@
 package docker
 
 import (
-	"bufio"
 	"bytes"
 	"encoding/json"
 	"errors"
 	"fmt"
 	"github.com/dotcloud/docker/auth"
-	"github.com/dotcloud/docker/fs"
 	"github.com/dotcloud/docker/future"
 	"github.com/dotcloud/docker/rcli"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"net/url"
-	"os"
 	"path"
 	"strconv"
 	"strings"
@@ -137,7 +133,7 @@ func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...str
 
 // 'docker info': display system-wide information.
 func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
-	images, _ := srv.images.Images()
+	images, _ := srv.containers.graph.All()
 	var imgcount int
 	if images == nil {
 		imgcount = 0
@@ -236,7 +232,7 @@ func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...strin
 	}
 	for _, name := range cmd.Args() {
 		if container := srv.containers.Get(name); container != nil {
-			if err := container.Mountpoint.EnsureMounted(); err != nil {
+			if err := container.EnsureMounted(); err != nil {
 				return err
 			}
 			fmt.Fprintln(stdout, container.Id)
@@ -260,7 +256,7 @@ func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...str
 	var obj interface{}
 	if container := srv.containers.Get(name); container != nil {
 		obj = container
-	} else if image, err := srv.images.Find(name); err != nil {
+	} else if image, err := srv.containers.graph.Get(name); err != nil {
 		return err
 	} else if image != nil {
 		obj = image
@@ -310,27 +306,12 @@ func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string
 // 'docker rmi NAME' removes all images with the name NAME
 func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) (err error) {
 	cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
-	fl_all := cmd.Bool("a", false, "Use IMAGE as a path and remove ALL images in this path")
-	fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name")
 	if cmd.Parse(args) != nil || cmd.NArg() < 1 {
 		cmd.Usage()
 		return nil
 	}
 	for _, name := range cmd.Args() {
-		if *fl_regexp {
-			err = srv.images.RemoveRegexp(name)
-		} else if *fl_all {
-			err = srv.images.RemoveInPath(name)
-		} else {
-			if image, err1 := srv.images.Find(name); err1 != nil {
-				err = err1
-			} else if err1 == nil && image == nil {
-				err = fmt.Errorf("No such image: %s", name)
-			} else {
-				err = srv.images.Remove(image)
-			}
-		}
-		if err != nil {
+		if err := srv.containers.graph.Delete(name); err != nil {
 			return err
 		}
 	}
@@ -409,7 +390,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri
 		archive = future.ProgressReader(resp.Body, int(resp.ContentLength), stdout)
 	}
 	fmt.Fprintf(stdout, "Unpacking to %s\n", name)
-	img, err := srv.images.Create(archive, nil, name, "")
+	img, err := srv.containers.graph.Create(archive, "", "")
 	if err != nil {
 		return err
 	}
@@ -419,7 +400,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri
 
 func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
-	limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
+	//limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
 	quiet := cmd.Bool("q", false, "only show numeric IDs")
 	if err := cmd.Parse(args); err != nil {
 		return nil
@@ -428,51 +409,65 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri
 		cmd.Usage()
 		return nil
 	}
-	var nameFilter string
-	if cmd.NArg() == 1 {
-		nameFilter = cmd.Arg(0)
-	}
+	/*
+		var nameFilter string
+		if cmd.NArg() == 1 {
+			nameFilter = cmd.Arg(0)
+		}
+	*/
 	w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
 	if !*quiet {
 		fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n")
 	}
-	paths, err := srv.images.Paths()
-	if err != nil {
-		return err
-	}
-	for _, name := range paths {
-		if nameFilter != "" && nameFilter != name {
-			continue
-		}
-		ids, err := srv.images.List(name)
+	if *quiet {
+		images, err := srv.containers.graph.All()
 		if err != nil {
 			return err
 		}
-		for idx, img := range ids {
-			if *limit > 0 && idx >= *limit {
-				break
-			}
-			if !*quiet {
-				for idx, field := range []string{
-					/* NAME */ name,
-					/* ID */ img.Id,
-					/* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
-					/* PARENT */ img.Parent,
-				} {
-					if idx == 0 {
-						w.Write([]byte(field))
-					} else {
-						w.Write([]byte("\t" + field))
-					}
-				}
-				w.Write([]byte{'\n'})
-			} else {
-				stdout.Write([]byte(img.Id + "\n"))
-			}
+		for _, image := range images {
+			fmt.Fprintln(stdout, image.Id)
 		}
-	}
-	if !*quiet {
-		w.Flush()
+	} else {
+		// FIXME:
+		//		paths, err := srv.images.Paths()
+		//		if err != nil {
+		//			return err
+		//		}
+		//		for _, name := range paths {
+		//			if nameFilter != "" && nameFilter != name {
+		//				continue
+		//			}
+		//			ids, err := srv.images.List(name)
+		//			if err != nil {
+		//				return err
+		//			}
+		//			for idx, img := range ids {
+		//				if *limit > 0 && idx >= *limit {
+		//					break
+		//				}
+		//				if !*quiet {
+		//					for idx, field := range []string{
+		//						/* NAME */ name,
+		//						/* ID */ img.Id,
+		//						/* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
+		//						/* PARENT */ img.Parent,
+		//					} {
+		//						if idx == 0 {
+		//							w.Write([]byte(field))
+		//						} else {
+		//							w.Write([]byte("\t" + field))
+		//						}
+		//					}
+		//					w.Write([]byte{'\n'})
+		//				} else {
+		//					stdout.Write([]byte(img.Id + "\n"))
+		//				}
+		//			}
+		//		}
+		//		if !*quiet {
+		//			w.Flush()
+		//		}
+		//
 	}
 	return nil
 
@@ -492,7 +487,6 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
 		fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n")
 	}
 	for _, container := range srv.containers.List() {
-		comment := container.GetUserData("comment")
 		if !container.State.Running && !*fl_all {
 			continue
 		}
@@ -503,11 +497,11 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
 			}
 			for idx, field := range []string{
 				/* ID */ container.Id,
-				/* IMAGE */ container.GetUserData("image"),
+				/* IMAGE */ container.Image,
 				/* COMMAND */ command,
 				/* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
 				/* STATUS */ container.State.String(),
-				/* COMMENT */ comment,
+				/* COMMENT */ "",
 			} {
 				if idx == 0 {
 					w.Write([]byte(field))
@@ -540,17 +534,13 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
 	}
 	if container := srv.containers.Get(containerName); container != nil {
 		// FIXME: freeze the container before copying it to avoid data corruption?
-		rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed)
+		// FIXME: this shouldn't be in commands.
+		rwTar, err := container.ExportRw()
 		if err != nil {
 			return err
 		}
 		// Create a new image from the container's base layers + a new layer from container changes
-		parentImg, err := srv.images.Get(container.Image)
-		if err != nil {
-			return err
-		}
-
-		img, err := srv.images.Create(rwTar, parentImg, imgName, "")
+		img, err := srv.containers.graph.Create(rwTar, container.Image, "")
 		if err != nil {
 			return err
 		}
@@ -574,10 +564,10 @@ func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string)
 	}
 	name := cmd.Arg(0)
 	if container := srv.containers.Get(name); container != nil {
-		if err := container.Mountpoint.EnsureMounted(); err != nil {
+		if err := container.EnsureMounted(); err != nil {
 			return err
 		}
-		data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed)
+		data, err := container.Export()
 		if err != nil {
 			return err
 		}
@@ -603,7 +593,7 @@ func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string
 	if container := srv.containers.Get(cmd.Arg(0)); container == nil {
 		return errors.New("No such container")
 	} else {
-		changes, err := srv.images.Changes(container.Mountpoint)
+		changes, err := container.Changes()
 		if err != nil {
 			return err
 		}
@@ -625,10 +615,20 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
 	}
 	name := cmd.Arg(0)
 	if container := srv.containers.Get(name); container != nil {
-		if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
+		log_stdout, err := container.ReadLog("stdout")
+		if err != nil {
 			return err
 		}
-		if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
+		log_stderr, err := container.ReadLog("stderr")
+		if err != nil {
+			return err
+		}
+		// FIXME: Interpolate stdout and stderr instead of concatenating them
+		// FIXME: Differentiate stdout and stderr in the remote protocol
+		if _, err := io.Copy(stdout, log_stdout); err != nil {
+			return err
+		}
+		if _, err := io.Copy(stdout, log_stderr); err != nil {
 			return err
 		}
 		return nil
@@ -636,31 +636,6 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
 	return errors.New("No such container: " + cmd.Arg(0))
 }
 
-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) {
-	id := future.RandomId()[:8]
-	container, err := srv.containers.Create(id, cmd, args, img,
-		&Config{
-			Hostname:  id,
-			Ports:     ports,
-			User:      user,
-			Tty:       tty,
-			OpenStdin: openStdin,
-			Memory:    memory,
-		})
-	if err != nil {
-		return nil, err
-	}
-	if err := container.SetUserData("image", img.Id); err != nil {
-		srv.containers.Destroy(container)
-		return nil, errors.New("Error setting container userdata: " + err.Error())
-	}
-	if err := container.SetUserData("comment", comment); err != nil {
-		srv.containers.Destroy(container)
-		return nil, errors.New("Error setting container userdata: " + err.Error())
-	}
-	return container, nil
-}
-
 func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container")
 	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)
 	fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
 	fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
 	fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
-	fl_comment := cmd.String("c", "", "Comment")
 	fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
 	var fl_ports ports
 
@@ -738,8 +712,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
 		return nil
 	}
 	name := cmd.Arg(0)
-	var img_name string
-	//var img_version string  // Only here for reference
 	var cmdline []string
 
 	if len(cmd.Args()) >= 2 {
@@ -758,33 +730,15 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
 		cmdline = []string{"/bin/bash", "-i"}
 	}
 
-	// Find the image
-	img, err := srv.images.Find(name)
-	if err != nil {
-		return err
-	} else if img == nil {
-		// Separate the name:version tag
-		if strings.Contains(name, ":") {
-			parts := strings.SplitN(name, ":", 2)
-			img_name = parts[0]
-			//img_version = parts[1]   // Only here for reference
-		} else {
-			img_name = name
-		}
-
-		stdin_noclose := ioutil.NopCloser(stdin)
-		if err := srv.CmdImport(stdin_noclose, stdout, img_name); err != nil {
-			return err
-		}
-		img, err = srv.images.Find(name)
-		if err != nil || img == nil {
-			return errors.New("Could not find image after downloading: " + name)
-		}
-	}
-
 	// Create new container
-	container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty,
-		*fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...)
+	container, err := srv.containers.Create(cmdline[0], cmdline[1:], name,
+		&Config{
+			Ports:     fl_ports,
+			User:      *fl_user,
+			Tty:       *fl_tty,
+			OpenStdin: *fl_stdin,
+			Memory:    *fl_memory,
+		})
 	if err != nil {
 		return errors.New("Error creating container: " + err.Error())
 	}
@@ -850,7 +804,6 @@ func NewServer() (*Server, error) {
 		return nil, err
 	}
 	srv := &Server{
-		images:     containers.Store,
 		containers: containers,
 	}
 	return srv, nil
@@ -858,5 +811,4 @@ func NewServer() (*Server, error) {
 
 type Server struct {
 	containers *Docker
-	images     *fs.Store
 }

+ 117 - 179
container.go

@@ -3,7 +3,9 @@ package docker
 import (
 	"encoding/json"
 	"errors"
-	"github.com/dotcloud/docker/fs"
+	"fmt"
+	"github.com/dotcloud/docker/future"
+	"github.com/dotcloud/docker/graph"
 	"github.com/kr/pty"
 	"io"
 	"io/ioutil"
@@ -16,40 +18,34 @@ import (
 	"time"
 )
 
-var sysInitPath string
-
-func init() {
-	sysInitPath = SelfPath()
-}
-
 type Container struct {
-	Id   string
-	Root string
+	root string
+
+	Id string
 
 	Created time.Time
 
 	Path string
 	Args []string
 
-	Config     *Config
-	Mountpoint *fs.Mountpoint
-	State      *State
-	Image      string
+	Config *Config
+	State  State
+	Image  string
 
 	network         *NetworkInterface
 	networkManager  *NetworkManager
 	NetworkSettings *NetworkSettings
 
-	SysInitPath   string
-	lxcConfigPath string
-	cmd           *exec.Cmd
-	stdout        *writeBroadcaster
-	stderr        *writeBroadcaster
-	stdin         io.ReadCloser
-	stdinPipe     io.WriteCloser
+	SysInitPath string
+	cmd         *exec.Cmd
+	stdout      *writeBroadcaster
+	stderr      *writeBroadcaster
+	stdin       io.ReadCloser
+	stdinPipe   io.WriteCloser
 
 	stdoutLog *os.File
 	stderrLog *os.File
+	runtime   *Docker // FIXME: rename Docker to Runtime for clarity
 }
 
 type Config struct {
@@ -69,104 +65,9 @@ type NetworkSettings struct {
 	PortMapping map[string]string
 }
 
-func createContainer(id string, root string, command string, args []string, image *fs.Image, config *Config, netManager *NetworkManager) (*Container, error) {
-	mountpoint, err := image.Mountpoint(path.Join(root, "rootfs"), path.Join(root, "rw"))
-	if err != nil {
-		return nil, err
-	}
-	container := &Container{
-		Id:              id,
-		Root:            root,
-		Created:         time.Now(),
-		Path:            command,
-		Args:            args,
-		Config:          config,
-		Image:           image.Id,
-		Mountpoint:      mountpoint,
-		State:           newState(),
-		networkManager:  netManager,
-		NetworkSettings: &NetworkSettings{},
-		SysInitPath:     sysInitPath,
-		lxcConfigPath:   path.Join(root, "config.lxc"),
-		stdout:          newWriteBroadcaster(),
-		stderr:          newWriteBroadcaster(),
-	}
-	if err := os.Mkdir(root, 0700); err != nil {
-		return nil, err
-	}
-	// Setup logging of stdout and stderr to disk
-	if stdoutLog, err := os.OpenFile(path.Join(container.Root, id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
-		return nil, err
-	} else {
-		container.stdoutLog = stdoutLog
-	}
-	if stderrLog, err := os.OpenFile(path.Join(container.Root, id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
-		return nil, err
-	} else {
-		container.stderrLog = stderrLog
-	}
-	if container.Config.OpenStdin {
-		container.stdin, container.stdinPipe = io.Pipe()
-	} else {
-		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
-	}
-	container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
-	container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
-
-	if err := container.save(); err != nil {
-		return nil, err
-	}
-	return container, nil
-}
-
-func loadContainer(store *fs.Store, containerPath string, netManager *NetworkManager) (*Container, error) {
-	data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
-	if err != nil {
-		return nil, err
-	}
-	mountpoint, err := store.FetchMountpoint(
-		path.Join(containerPath, "rootfs"),
-		path.Join(containerPath, "rw"),
-	)
-	if err != nil {
-		return nil, err
-	} else if mountpoint == nil {
-		return nil, errors.New("Couldn't load container: unregistered mountpoint.")
-	}
-	container := &Container{
-		stdout:          newWriteBroadcaster(),
-		stderr:          newWriteBroadcaster(),
-		lxcConfigPath:   path.Join(containerPath, "config.lxc"),
-		networkManager:  netManager,
-		NetworkSettings: &NetworkSettings{},
-		Mountpoint:      mountpoint,
-	}
-	// Load container settings
-	if err := json.Unmarshal(data, container); err != nil {
-		return nil, err
-	}
-
-	// Setup logging of stdout and stderr to disk
-	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 {
-		return nil, err
-	} else {
-		container.stdoutLog = stdoutLog
-	}
-	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 {
-		return nil, err
-	} else {
-		container.stderrLog = stderrLog
-	}
-	container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
-	container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
-
-	if container.Config.OpenStdin {
-		container.stdin, container.stdinPipe = io.Pipe()
-	} else {
-		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
-	}
-	container.State = newState()
-	return container, nil
+func GenerateId() string {
+	future.Seed()
+	return future.RandomId()
 }
 
 func (container *Container) Cmd() *exec.Cmd {
@@ -177,64 +78,32 @@ func (container *Container) When() time.Time {
 	return container.Created
 }
 
-func (container *Container) loadUserData() (map[string]string, error) {
-	jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
-	if err != nil {
-		if os.IsNotExist(err) {
-			return make(map[string]string), nil
-		}
-		return nil, err
-	}
-	data := make(map[string]string)
-	if err := json.Unmarshal(jsonData, &data); err != nil {
-		return nil, err
-	}
-	return data, nil
-}
-
-func (container *Container) saveUserData(data map[string]string) error {
-	jsonData, err := json.Marshal(data)
+func (container *Container) FromDisk() error {
+	data, err := ioutil.ReadFile(container.jsonPath())
 	if err != nil {
 		return err
 	}
-	return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700)
-}
-
-func (container *Container) SetUserData(key, value string) error {
-	data, err := container.loadUserData()
-	if err != nil {
+	// Load container settings
+	if err := json.Unmarshal(data, container); err != nil {
 		return err
 	}
-	data[key] = value
-	return container.saveUserData(data)
-}
-
-func (container *Container) GetUserData(key string) string {
-	data, err := container.loadUserData()
-	if err != nil {
-		return ""
-	}
-	if value, exists := data[key]; exists {
-		return value
-	}
-	return ""
+	return nil
 }
 
-func (container *Container) save() (err error) {
+func (container *Container) ToDisk() (err error) {
 	data, err := json.Marshal(container)
 	if err != nil {
 		return
 	}
-	return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0666)
+	return ioutil.WriteFile(container.jsonPath(), data, 0666)
 }
 
 func (container *Container) generateLXCConfig() error {
-	fo, err := os.Create(container.lxcConfigPath)
+	fo, err := os.Create(container.lxcConfigPath())
 	if err != nil {
 		return err
 	}
 	defer fo.Close()
-
 	if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
 		return err
 	}
@@ -309,7 +178,7 @@ func (container *Container) start() error {
 }
 
 func (container *Container) Start() error {
-	if err := container.Mountpoint.EnsureMounted(); err != nil {
+	if err := container.EnsureMounted(); err != nil {
 		return err
 	}
 	if err := container.allocateNetwork(); err != nil {
@@ -320,7 +189,7 @@ func (container *Container) Start() error {
 	}
 	params := []string{
 		"-n", container.Id,
-		"-f", container.lxcConfigPath,
+		"-f", container.lxcConfigPath(),
 		"--",
 		"/sbin/init",
 	}
@@ -348,8 +217,10 @@ func (container *Container) Start() error {
 	if err != nil {
 		return err
 	}
+	// FIXME: save state on disk *first*, then converge
+	// this way disk state is used as a journal, eg. we can restore after crash etc.
 	container.State.setRunning(container.cmd.Process.Pid)
-	container.save()
+	container.ToDisk()
 	go container.monitor()
 	return nil
 }
@@ -389,28 +260,12 @@ func (container *Container) StdoutPipe() (io.ReadCloser, error) {
 	return newBufReader(reader), nil
 }
 
-func (container *Container) StdoutLog() io.Reader {
-	r, err := os.Open(container.stdoutLog.Name())
-	if err != nil {
-		return nil
-	}
-	return r
-}
-
 func (container *Container) StderrPipe() (io.ReadCloser, error) {
 	reader, writer := io.Pipe()
 	container.stderr.AddWriter(writer)
 	return newBufReader(reader), nil
 }
 
-func (container *Container) StderrLog() io.Reader {
-	r, err := os.Open(container.stderrLog.Name())
-	if err != nil {
-		return nil
-	}
-	return r
-}
-
 func (container *Container) allocateNetwork() error {
 	iface, err := container.networkManager.Allocate()
 	if err != nil {
@@ -450,7 +305,7 @@ func (container *Container) monitor() {
 	}
 	container.stdout.Close()
 	container.stderr.Close()
-	if err := container.Mountpoint.Umount(); err != nil {
+	if err := container.Unmount(); err != nil {
 		log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
 	}
 
@@ -461,7 +316,7 @@ func (container *Container) monitor() {
 
 	// Report status back
 	container.State.setStopped(exitCode)
-	container.save()
+	container.ToDisk()
 }
 
 func (container *Container) kill() error {
@@ -523,6 +378,17 @@ func (container *Container) Wait() int {
 	return container.State.ExitCode
 }
 
+func (container *Container) ExportRw() (graph.Archive, error) {
+	return graph.Tar(container.rwPath(), graph.Uncompressed)
+}
+
+func (container *Container) Export() (graph.Archive, error) {
+	if err := container.EnsureMounted(); err != nil {
+		return nil, err
+	}
+	return graph.Tar(container.RootfsPath(), graph.Uncompressed)
+}
+
 func (container *Container) WaitTimeout(timeout time.Duration) error {
 	done := make(chan bool)
 	go func() {
@@ -538,3 +404,75 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
 	}
 	return nil
 }
+
+func (container *Container) EnsureMounted() error {
+	if mounted, err := container.Mounted(); err != nil {
+		return err
+	} else if mounted {
+		return nil
+	}
+	return container.Mount()
+}
+
+func (container *Container) Mount() error {
+	image, err := container.GetImage()
+	if err != nil {
+		return err
+	}
+	return image.Mount(container.RootfsPath(), container.rwPath())
+}
+
+func (container *Container) Changes() ([]graph.Change, error) {
+	image, err := container.GetImage()
+	if err != nil {
+		return nil, err
+	}
+	return image.Changes(container.rwPath())
+}
+
+func (container *Container) GetImage() (*graph.Image, error) {
+	if container.runtime == nil {
+		return nil, fmt.Errorf("Can't get image of unregistered container")
+	}
+	return container.runtime.graph.Get(container.Image)
+}
+
+func (container *Container) Mounted() (bool, error) {
+	return graph.Mounted(container.RootfsPath())
+}
+
+func (container *Container) Unmount() error {
+	return graph.Unmount(container.RootfsPath())
+}
+
+func (container *Container) logPath(name string) string {
+	return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
+}
+
+func (container *Container) ReadLog(name string) (io.Reader, error) {
+	return os.Open(container.logPath(name))
+}
+
+func (container *Container) jsonPath() string {
+	return path.Join(container.root, "config.json")
+}
+
+func (container *Container) lxcConfigPath() string {
+	return path.Join(container.root, "config.lxc")
+}
+
+// This method must be exported to be used from the lxc template
+func (container *Container) RootfsPath() string {
+	return path.Join(container.root, "rootfs")
+}
+
+func (container *Container) rwPath() string {
+	return path.Join(container.root, "rw")
+}
+
+func validateId(id string) error {
+	if id == "" {
+		return fmt.Errorf("Invalid empty id")
+	}
+	return nil
+}

+ 27 - 57
container_test.go

@@ -3,7 +3,6 @@ package docker
 import (
 	"bufio"
 	"fmt"
-	"github.com/dotcloud/docker/fs"
 	"io"
 	"io/ioutil"
 	"math/rand"
@@ -21,10 +20,9 @@ func TestCommitRun(t *testing.T) {
 	}
 	defer nuke(docker)
 	container1, err := docker.Create(
-		"precommit_test",
 		"/bin/sh",
 		[]string{"-c", "echo hello > /world"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			Memory: 33554432,
 		},
@@ -44,18 +42,11 @@ func TestCommitRun(t *testing.T) {
 		t.Errorf("Container shouldn't be running")
 	}
 
-	// FIXME: freeze the container before copying it to avoid data corruption?
-	rwTar, err := fs.Tar(container1.Mountpoint.Rw, fs.Uncompressed)
+	rwTar, err := container1.ExportRw()
 	if err != nil {
 		t.Error(err)
 	}
-	// Create a new image from the container's base layers + a new layer from container changes
-	parentImg, err := docker.Store.Get(container1.Image)
-	if err != nil {
-		t.Error(err)
-	}
-
-	img, err := docker.Store.Create(rwTar, parentImg, "test_commitrun", "unit test commited image")
+	img, err := docker.graph.Create(rwTar, container1.Image, "unit test commited image")
 	if err != nil {
 		t.Error(err)
 	}
@@ -63,10 +54,9 @@ func TestCommitRun(t *testing.T) {
 	// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
 
 	container2, err := docker.Create(
-		"postcommit_test",
 		"cat",
 		[]string{"/world"},
-		img,
+		img.Id,
 		&Config{
 			Memory: 33554432,
 		},
@@ -98,10 +88,9 @@ func TestRun(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"run_test",
 		"ls",
 		[]string{"-al"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			Memory: 33554432,
 		},
@@ -129,10 +118,9 @@ func TestOutput(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"output_test",
 		"echo",
 		[]string{"-n", "foobar"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -155,10 +143,9 @@ func TestKill(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"stop_test",
 		"cat",
 		[]string{"/dev/zero"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -199,10 +186,9 @@ func TestExitCode(t *testing.T) {
 	defer nuke(docker)
 
 	trueContainer, err := docker.Create(
-		"exit_test_1",
 		"/bin/true",
 		[]string{""},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -214,10 +200,9 @@ func TestExitCode(t *testing.T) {
 	}
 
 	falseContainer, err := docker.Create(
-		"exit_test_2",
 		"/bin/false",
 		[]string{""},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -244,10 +229,9 @@ func TestRestart(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"restart_test",
 		"echo",
 		[]string{"-n", "foobar"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -279,10 +263,9 @@ func TestRestartStdin(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"restart_stdin_test",
 		"cat",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			OpenStdin: true,
 		},
@@ -331,10 +314,9 @@ func TestUser(t *testing.T) {
 
 	// Default user must be root
 	container, err := docker.Create(
-		"user_default",
 		"id",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -351,10 +333,9 @@ func TestUser(t *testing.T) {
 
 	// Set a username
 	container, err = docker.Create(
-		"user_root",
 		"id",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			User: "root",
 		},
@@ -373,10 +354,9 @@ func TestUser(t *testing.T) {
 
 	// Set a UID
 	container, err = docker.Create(
-		"user_uid0",
 		"id",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			User: "0",
 		},
@@ -395,10 +375,9 @@ func TestUser(t *testing.T) {
 
 	// Set a different user by uid
 	container, err = docker.Create(
-		"user_uid1",
 		"id",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			User: "1",
 		},
@@ -419,10 +398,9 @@ func TestUser(t *testing.T) {
 
 	// Set a different user by username
 	container, err = docker.Create(
-		"user_daemon",
 		"id",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			User: "daemon",
 		},
@@ -448,10 +426,9 @@ func TestMultipleContainers(t *testing.T) {
 	defer nuke(docker)
 
 	container1, err := docker.Create(
-		"container1",
 		"cat",
 		[]string{"/dev/zero"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -460,10 +437,9 @@ func TestMultipleContainers(t *testing.T) {
 	defer docker.Destroy(container1)
 
 	container2, err := docker.Create(
-		"container2",
 		"cat",
 		[]string{"/dev/zero"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -504,10 +480,9 @@ func TestStdin(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"stdin_test",
 		"cat",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			OpenStdin: true,
 		},
@@ -540,10 +515,9 @@ func TestTty(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"tty_test",
 		"cat",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			OpenStdin: true,
 		},
@@ -576,10 +550,9 @@ func TestEnv(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"env_test",
 		"/usr/bin/env",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -651,10 +624,9 @@ func TestLXCConfig(t *testing.T) {
 	memMax := 536870912
 	mem := memMin + rand.Intn(memMax-memMin)
 	container, err := docker.Create(
-		"config_test",
 		"/bin/true",
 		[]string{},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{
 			Hostname: "foobar",
 			Memory:   int64(mem),
@@ -665,10 +637,10 @@ func TestLXCConfig(t *testing.T) {
 	}
 	defer docker.Destroy(container)
 	container.generateLXCConfig()
-	grepFile(t, container.lxcConfigPath, "lxc.utsname = foobar")
-	grepFile(t, container.lxcConfigPath,
+	grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar")
+	grepFile(t, container.lxcConfigPath(),
 		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
-	grepFile(t, container.lxcConfigPath,
+	grepFile(t, container.lxcConfigPath(),
 		fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
 }
 
@@ -680,10 +652,9 @@ func BenchmarkRunSequencial(b *testing.B) {
 	defer nuke(docker)
 	for i := 0; i < b.N; i++ {
 		container, err := docker.Create(
-			fmt.Sprintf("bench_%v", i),
 			"echo",
 			[]string{"-n", "foo"},
-			GetTestImage(docker),
+			GetTestImage(docker).Id,
 			&Config{},
 		)
 		if err != nil {
@@ -717,10 +688,9 @@ func BenchmarkRunParallel(b *testing.B) {
 		tasks = append(tasks, complete)
 		go func(i int, complete chan error) {
 			container, err := docker.Create(
-				fmt.Sprintf("bench_%v", i),
 				"echo",
 				[]string{"-n", "foo"},
-				GetTestImage(docker),
+				GetTestImage(docker).Id,
 				&Config{},
 			)
 			if err != nil {

+ 110 - 22
docker.go

@@ -3,12 +3,15 @@ package docker
 import (
 	"container/list"
 	"fmt"
-	"github.com/dotcloud/docker/fs"
+	"github.com/dotcloud/docker/graph"
+	"io"
 	"io/ioutil"
 	"log"
 	"os"
 	"path"
 	"sort"
+	"sync"
+	"time"
 )
 
 type Docker struct {
@@ -16,7 +19,13 @@ type Docker struct {
 	repository     string
 	containers     *list.List
 	networkManager *NetworkManager
-	Store          *fs.Store
+	graph          *graph.Graph
+}
+
+var sysInitPath string
+
+func init() {
+	sysInitPath = SelfPath()
 }
 
 func (docker *Docker) List() []*Container {
@@ -49,20 +58,98 @@ func (docker *Docker) Exists(id string) bool {
 	return docker.Get(id) != nil
 }
 
-func (docker *Docker) Create(id string, command string, args []string, image *fs.Image, config *Config) (*Container, error) {
-	if docker.Exists(id) {
-		return nil, fmt.Errorf("Container %v already exists", id)
+func (docker *Docker) containerRoot(id string) string {
+	return path.Join(docker.repository, id)
+}
+
+func (docker *Docker) Create(command string, args []string, image string, config *Config) (*Container, error) {
+	container := &Container{
+		// FIXME: we should generate the ID here instead of receiving it as an argument
+		Id:              GenerateId(),
+		Created:         time.Now(),
+		Path:            command,
+		Args:            args,
+		Config:          config,
+		Image:           image,
+		NetworkSettings: &NetworkSettings{},
+		// FIXME: do we need to store this in the container?
+		SysInitPath: sysInitPath,
+	}
+	container.root = docker.containerRoot(container.Id)
+	// Step 1: create the container directory.
+	// This doubles as a barrier to avoid race conditions.
+	if err := os.Mkdir(container.root, 0700); err != nil {
+		return nil, err
+	}
+	// Step 2: save the container json
+	if err := container.ToDisk(); err != nil {
+		return nil, err
+	}
+	// Step 3: register the container
+	if err := docker.Register(container); err != nil {
+		return nil, err
 	}
-	root := path.Join(docker.repository, id)
+	return container, nil
+}
 
-	container, err := createContainer(id, root, command, args, image, config, docker.networkManager)
-	if err != nil {
+func (docker *Docker) Load(id string) (*Container, error) {
+	container := &Container{root: docker.containerRoot(id)}
+	if err := container.FromDisk(); err != nil {
+		return nil, err
+	}
+	if container.Id != id {
+		return container, fmt.Errorf("Container %s is stored at %s", container.Id, id)
+	}
+	if err := docker.Register(container); err != nil {
 		return nil, err
 	}
-	docker.containers.PushBack(container)
 	return container, nil
 }
 
+// Register makes a container object usable by the runtime as <container.Id>
+func (docker *Docker) Register(container *Container) error {
+	if container.runtime != nil || docker.Exists(container.Id) {
+		return fmt.Errorf("Container is already loaded")
+	}
+	if err := validateId(container.Id); err != nil {
+		return err
+	}
+	container.runtime = docker
+	container.networkManager = docker.networkManager // FIXME: infer from docker.runtime
+	// Setup state lock (formerly in newState()
+	lock := new(sync.Mutex)
+	container.State.stateChangeLock = lock
+	container.State.stateChangeCond = sync.NewCond(lock)
+	// Attach to stdout and stderr
+	container.stderr = newWriteBroadcaster()
+	container.stdout = newWriteBroadcaster()
+	// Attach to stdin
+	if container.Config.OpenStdin {
+		container.stdin, container.stdinPipe = io.Pipe()
+	} else {
+		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
+	}
+	// Setup logging of stdout and stderr to disk
+	if err := docker.LogToDisk(container.stdout, container.logPath("stdout")); err != nil {
+		return err
+	}
+	if err := docker.LogToDisk(container.stderr, container.logPath("stderr")); err != nil {
+		return err
+	}
+	// done
+	docker.containers.PushBack(container)
+	return nil
+}
+
+func (docker *Docker) LogToDisk(src *writeBroadcaster, dst string) error {
+	log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
+	if err != nil {
+		return err
+	}
+	src.AddWriter(NopWriteCloser(log))
+	return nil
+}
+
 func (docker *Docker) Destroy(container *Container) error {
 	element := docker.getContainerElement(container.Id)
 	if element == nil {
@@ -72,18 +159,18 @@ func (docker *Docker) Destroy(container *Container) error {
 	if err := container.Stop(); err != nil {
 		return err
 	}
-	if container.Mountpoint.Mounted() {
-		if err := container.Mountpoint.Umount(); err != nil {
-			return fmt.Errorf("Unable to umount container %v: %v", container.Id, err)
+	if mounted, err := container.Mounted(); err != nil {
+		return err
+	} else if mounted {
+		if err := container.Unmount(); err != nil {
+			return fmt.Errorf("Unable to unmount container %v: %v", container.Id, err)
 		}
 	}
-	if err := container.Mountpoint.Deregister(); err != nil {
-		return fmt.Errorf("Unable to deregiser -- ? mountpoint %v: %v", container.Mountpoint.Root, err)
-	}
-	if err := os.RemoveAll(container.Root); err != nil {
+	// Deregister the container before removing its directory, to avoid race conditions
+	docker.containers.Remove(element)
+	if err := os.RemoveAll(container.root); err != nil {
 		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err)
 	}
-	docker.containers.Remove(element)
 	return nil
 }
 
@@ -93,12 +180,13 @@ func (docker *Docker) restore() error {
 		return err
 	}
 	for _, v := range dir {
-		container, err := loadContainer(docker.Store, path.Join(docker.repository, v.Name()), docker.networkManager)
+		id := v.Name()
+		container, err := docker.Load(id)
 		if err != nil {
-			log.Printf("Failed to load container %v: %v", v.Name(), err)
+			log.Printf("Failed to load container %v: %v", id, err)
 			continue
 		}
-		docker.containers.PushBack(container)
+		log.Printf("Loaded container %v", container.Id)
 	}
 	return nil
 }
@@ -114,7 +202,7 @@ func NewFromDirectory(root string) (*Docker, error) {
 		return nil, err
 	}
 
-	store, err := fs.New(path.Join(root, "images"))
+	graph, err := graph.New(path.Join(root, "graph"))
 	if err != nil {
 		return nil, err
 	}
@@ -127,8 +215,8 @@ func NewFromDirectory(root string) (*Docker, error) {
 		root:           root,
 		repository:     docker_repo,
 		containers:     list.New(),
-		Store:          store,
 		networkManager: netManager,
+		graph:          graph,
 	}
 
 	if err := docker.restore(); err != nil {

+ 22 - 29
docker_test.go

@@ -1,7 +1,7 @@
 package docker
 
 import (
-	"github.com/dotcloud/docker/fs"
+	"github.com/dotcloud/docker/graph"
 	"io"
 	"io/ioutil"
 	"os"
@@ -63,7 +63,6 @@ func init() {
 	}
 	// Create the "Server"
 	srv := &Server{
-		images:     docker.Store,
 		containers: docker,
 	}
 	// Retrieve the Image
@@ -93,8 +92,8 @@ func newTestDocker() (*Docker, error) {
 	return docker, nil
 }
 
-func GetTestImage(docker *Docker) *fs.Image {
-	imgs, err := docker.Store.Images()
+func GetTestImage(docker *Docker) *graph.Image {
+	imgs, err := docker.graph.All()
 	if err != nil {
 		panic(err)
 	} else if len(imgs) < 1 {
@@ -115,10 +114,9 @@ func TestCreate(t *testing.T) {
 		t.Errorf("Expected 0 containers, %v found", len(docker.List()))
 	}
 	container, err := docker.Create(
-		"test_create",
 		"ls",
 		[]string{"-al"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -137,22 +135,22 @@ func TestCreate(t *testing.T) {
 	}
 
 	// Make sure the container List() returns is the right one
-	if docker.List()[0].Id != "test_create" {
+	if docker.List()[0].Id != container.Id {
 		t.Errorf("Unexpected container %v returned by List", docker.List()[0])
 	}
 
 	// Make sure we can get the container with Get()
-	if docker.Get("test_create") == nil {
+	if docker.Get(container.Id) == nil {
 		t.Errorf("Unable to get newly created container")
 	}
 
 	// Make sure it is the right container
-	if docker.Get("test_create") != container {
+	if docker.Get(container.Id) != container {
 		t.Errorf("Get() returned the wrong container")
 	}
 
 	// Make sure Exists returns it as existing
-	if !docker.Exists("test_create") {
+	if !docker.Exists(container.Id) {
 		t.Errorf("Exists() returned false for a newly created container")
 	}
 }
@@ -164,10 +162,9 @@ func TestDestroy(t *testing.T) {
 	}
 	defer nuke(docker)
 	container, err := docker.Create(
-		"test_destroy",
 		"ls",
 		[]string{"-al"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -189,12 +186,12 @@ func TestDestroy(t *testing.T) {
 	}
 
 	// Make sure docker.Get() refuses to return the unexisting container
-	if docker.Get("test_destroy") != nil {
+	if docker.Get(container.Id) != nil {
 		t.Errorf("Unable to get newly created container")
 	}
 
 	// Make sure the container root directory does not exist anymore
-	_, err = os.Stat(container.Root)
+	_, err = os.Stat(container.root)
 	if err == nil || !os.IsNotExist(err) {
 		t.Errorf("Container root directory still exists after destroy")
 	}
@@ -213,10 +210,9 @@ func TestGet(t *testing.T) {
 	}
 	defer nuke(docker)
 	container1, err := docker.Create(
-		"test1",
 		"ls",
 		[]string{"-al"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -225,10 +221,9 @@ func TestGet(t *testing.T) {
 	defer docker.Destroy(container1)
 
 	container2, err := docker.Create(
-		"test2",
 		"ls",
 		[]string{"-al"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -237,10 +232,9 @@ func TestGet(t *testing.T) {
 	defer docker.Destroy(container2)
 
 	container3, err := docker.Create(
-		"test3",
 		"ls",
 		[]string{"-al"},
-		GetTestImage(docker),
+		GetTestImage(docker).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -248,16 +242,16 @@ func TestGet(t *testing.T) {
 	}
 	defer docker.Destroy(container3)
 
-	if docker.Get("test1") != container1 {
-		t.Errorf("Get(test1) returned %v while expecting %v", docker.Get("test1"), container1)
+	if docker.Get(container1.Id) != container1 {
+		t.Errorf("Get(test1) returned %v while expecting %v", docker.Get(container1.Id), container1)
 	}
 
-	if docker.Get("test2") != container2 {
-		t.Errorf("Get(test2) returned %v while expecting %v", docker.Get("test2"), container2)
+	if docker.Get(container2.Id) != container2 {
+		t.Errorf("Get(test2) returned %v while expecting %v", docker.Get(container2.Id), container2)
 	}
 
-	if docker.Get("test3") != container3 {
-		t.Errorf("Get(test3) returned %v while expecting %v", docker.Get("test3"), container3)
+	if docker.Get(container3.Id) != container3 {
+		t.Errorf("Get(test3) returned %v while expecting %v", docker.Get(container3.Id), container3)
 	}
 
 }
@@ -282,10 +276,9 @@ func TestRestore(t *testing.T) {
 
 	// Create a container with one instance of docker
 	container1, err := docker1.Create(
-		"restore_test",
 		"ls",
 		[]string{"-al"},
-		GetTestImage(docker1),
+		GetTestImage(docker1).Id,
 		&Config{},
 	)
 	if err != nil {
@@ -309,7 +302,7 @@ func TestRestore(t *testing.T) {
 	if len(docker2.List()) != 1 {
 		t.Errorf("Expected 1 container, %v found", len(docker2.List()))
 	}
-	container2 := docker2.Get("restore_test")
+	container2 := docker2.Get(container1.Id)
 	if container2 == nil {
 		t.Fatal("Unable to Get container")
 	}

+ 1 - 1
lxc_template.go

@@ -22,7 +22,7 @@ lxc.network.mtu = 1500
 lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}}
 
 # root filesystem
-{{$ROOTFS := .Mountpoint.Root}}
+{{$ROOTFS := .RootfsPath}}
 lxc.rootfs = {{$ROOTFS}}
 
 # use a dedicated pts for the container (and limit the number of pseudo terminal

+ 0 - 8
state.go

@@ -17,14 +17,6 @@ type State struct {
 	stateChangeCond *sync.Cond
 }
 
-func newState() *State {
-	lock := new(sync.Mutex)
-	return &State{
-		stateChangeLock: lock,
-		stateChangeCond: sync.NewCond(lock),
-	}
-}
-
 // String returns a human-readable description of the state
 func (s *State) String() string {
 	if s.Running {