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

This commit is contained in:
Solomon Hykes 2013-01-25 11:32:37 -08:00
Родитель 63fc3e5ab3
Коммит c885a05bdf
2 изменённых файлов: 169 добавлений и 9 удалений

Просмотреть файл

@ -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
future/layers.go Normal file
Просмотреть файл

@ -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)
}