Jelajahi Sumber

First implementation of a layer store. 'docker pull' and 'docker put' now really work (but containers are still fake)

Solomon Hykes 12 tahun lalu
induk
melakukan
c885a05bdf
2 mengubah file dengan 169 tambahan dan 9 penghapusan
  1. 34 9
      dockerd/dockerd.go
  2. 135 0
      future/layers.go

+ 34 - 9
dockerd/dockerd.go

@@ -19,6 +19,7 @@ import (
 	"sort"
 	"os"
 	"time"
+	"net/http"
 )
 
 
@@ -138,9 +139,16 @@ func (docker *Docker) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...str
 	if len(args) < 1 {
 		return errors.New("Not enough arguments")
 	}
-	time.Sleep(2 * time.Second)
-	layer := docker.addContainer(args[0], "download", 0)
-	fmt.Fprintln(stdout, layer.Id)
+	resp, err := http.Get(args[0])
+	if err != nil {
+		return err
+	}
+	layer, err := docker.layers.AddLayer(resp.Body, stdout)
+	if err != nil {
+		return err
+	}
+	docker.addContainer(args[0], "download", 0)
+	fmt.Fprintln(stdout, layer.Id())
 	return nil
 }
 
@@ -148,12 +156,17 @@ func (docker *Docker) CmdPut(stdin io.ReadCloser, stdout io.Writer, args ...stri
 	if len(args) < 1 {
 		return errors.New("Not enough arguments")
 	}
-	time.Sleep(1 * time.Second)
-	layer := docker.addContainer(args[0], "upload", 0)
-	fmt.Fprintln(stdout, layer.Id)
+	fmt.Printf("Adding layer\n")
+	layer, err := docker.layers.AddLayer(stdin, stdout)
+	if err != nil {
+		return err
+	}
+	docker.addContainer(args[0], "upload", 0)
+	fmt.Fprintln(stdout, layer.Id())
 	return nil
 }
 
+
 func (docker *Docker) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	flags := rcli.Subcmd(stdout,
 		"fork", "[OPTIONS] CONTAINER [DEST]",
@@ -391,7 +404,10 @@ func startCommand(cmd *exec.Cmd, interactive bool) (io.WriteCloser, io.ReadClose
 func main() {
 	future.Seed()
 	flag.Parse()
-	docker := New()
+	docker, err := New()
+	if err != nil {
+		log.Fatal(err)
+	}
 	go func() {
 		if err := rcli.ListenAndServeHTTP(":8080", docker); err != nil {
 			log.Fatal(err)
@@ -402,11 +418,19 @@ func main() {
 	}
 }
 
-func New() *Docker {
+func New() (*Docker, error) {
+	store, err := future.NewStore("/var/lib/docker/layers")
+	if err != nil {
+		return nil, err
+	}
+	if err := store.Init(); err != nil {
+		return nil, err
+	}
 	return &Docker{
 		containersByName: make(map[string]*ByDate),
 		containers: make(map[string]*Container),
-	}
+		layers: store,
+	}, nil
 }
 
 
@@ -453,6 +477,7 @@ func (docker *Docker) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...stri
 type Docker struct {
 	containers		map[string]*Container
 	containersByName	map[string]*ByDate
+	layers			*future.Store
 }
 
 type Container struct {

+ 135 - 0
future/layers.go

@@ -0,0 +1,135 @@
+package future
+
+import (
+	"errors"
+	"path"
+	"path/filepath"
+	"io"
+	"os"
+	"os/exec"
+)
+
+type Store struct {
+	Root	string
+}
+
+
+func NewStore(root string) (*Store, error) {
+	abspath, err := filepath.Abs(root)
+	if err != nil {
+		return nil, err
+	}
+	return &Store{
+		Root: abspath,
+	}, nil
+}
+
+func (store *Store) Get(id string) (*Layer, bool) {
+	layer := &Layer{Path: store.layerPath(id)}
+	if !layer.Exists() {
+		return nil, false
+	}
+	return layer, true
+}
+
+func (store *Store) Exists() (bool, error) {
+	if stat, err := os.Stat(store.Root); err != nil {
+		if os.IsNotExist(err) {
+			return false, nil
+		}
+		return false, err
+	} else if !stat.IsDir() {
+		return false, errors.New("Not a directory: " + store.Root)
+	}
+	return true, nil
+}
+
+func (store *Store) Init() error {
+	if exists, err := store.Exists(); err != nil {
+		return err
+	} else if exists {
+		return nil
+	}
+	return os.Mkdir(store.Root, 0700)
+}
+
+
+func (store *Store) Mktemp() (string, error) {
+	tmpName := RandomId()
+	tmpPath := path.Join(store.Root, "tmp-" + tmpName)
+	if err := os.Mkdir(tmpPath, 0700); err != nil {
+		return "", err
+	}
+	return tmpPath, nil
+}
+
+func (store *Store) layerPath(id string) string {
+	return path.Join(store.Root, id)
+}
+
+
+func (store *Store) AddLayer(archive io.Reader, stderr io.Writer) (*Layer, error) {
+	tmp, err := store.Mktemp()
+	defer os.RemoveAll(tmp)
+	if err != nil {
+		return nil, err
+	}
+	untarCmd := exec.Command("tar", "-C", tmp, "-x")
+	untarW, err := untarCmd.StdinPipe()
+	if err != nil {
+		return nil, err
+	}
+	untarStderr, err := untarCmd.StderrPipe()
+	if err != nil {
+		return nil, err
+	}
+	go io.Copy(stderr, untarStderr)
+	untarStdout, err := untarCmd.StdoutPipe()
+	if err != nil {
+		return nil, err
+	}
+	go io.Copy(stderr, untarStdout)
+	untarCmd.Start()
+	hashR, hashW := io.Pipe()
+	job_copy := Go(func() error {
+		_, err := io.Copy(io.MultiWriter(hashW, untarW), archive)
+		hashW.Close()
+		untarW.Close()
+		return err
+	})
+	id, err := ComputeId(hashR)
+	if err != nil {
+		return nil, err
+	}
+	if err := untarCmd.Wait(); err != nil {
+		return nil, err
+	}
+	if err := <-job_copy; err != nil {
+		return nil, err
+	}
+	layer := &Layer{Path: store.layerPath(id)}
+	if !layer.Exists() {
+		if err := os.Rename(tmp, layer.Path); err != nil {
+			return nil, err
+		}
+	}
+
+	return layer, nil
+}
+
+
+type Layer struct {
+	Path	string
+}
+
+func (layer *Layer) Exists() bool {
+	st, err := os.Stat(layer.Path)
+	if err != nil {
+		return false
+	}
+	return st.IsDir()
+}
+
+func (layer *Layer) Id() string {
+	return path.Base(layer.Path)
+}