Browse Source

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

Solomon Hykes 12 năm trước cách đây
mục cha
commit
7c57a4cfc0
7 tập tin đã thay đổi với 362 bổ sung429 xóa
  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 {