mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
docker/fs: initial support for filesystem layers (adapted from image/layers.go)
This commit is contained in:
parent
1531848ca6
commit
6372a1a0d0
3 changed files with 103 additions and 7 deletions
|
@ -1,4 +1,4 @@
|
||||||
package image
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -20,6 +20,10 @@ func NewLayerStore(root string) (*LayerStore, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Create the root directory if it doesn't exists
|
||||||
|
if err := os.Mkdir(root, 0700); err != nil && !os.IsExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &LayerStore{
|
return &LayerStore{
|
||||||
Root: abspath,
|
Root: abspath,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -82,7 +86,10 @@ func (store *LayerStore) layerPath(id string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (store *LayerStore) AddLayer(archive io.Reader, stderr io.Writer, compression Compression) (string, error) {
|
func (store *LayerStore) AddLayer(id string, archive Archive, stderr io.Writer, compression Compression) (string, error) {
|
||||||
|
if _, err := os.Stat(store.layerPath(id)); err == nil {
|
||||||
|
return "", errors.New("Layer already exists: " + id)
|
||||||
|
}
|
||||||
tmp, err := store.Mktemp()
|
tmp, err := store.Mktemp()
|
||||||
defer os.RemoveAll(tmp)
|
defer os.RemoveAll(tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -110,14 +117,11 @@ func (store *LayerStore) AddLayer(archive io.Reader, stderr io.Writer, compressi
|
||||||
}
|
}
|
||||||
go io.Copy(stderr, untarStdout)
|
go io.Copy(stderr, untarStdout)
|
||||||
untarCmd.Start()
|
untarCmd.Start()
|
||||||
hashR, hashW := io.Pipe()
|
|
||||||
job_copy := future.Go(func() error {
|
job_copy := future.Go(func() error {
|
||||||
_, err := io.Copy(io.MultiWriter(hashW, untarW), archive)
|
_, err := io.Copy(untarW, archive)
|
||||||
hashW.Close()
|
|
||||||
untarW.Close()
|
untarW.Close()
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
id, err := future.ComputeId(hashR)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
80
fs/layers_test.go
Normal file
80
fs/layers_test.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
"os"
|
||||||
|
"github.com/dotcloud/docker/fake"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func TestLayersInit(t *testing.T) {
|
||||||
|
store := tempStore(t)
|
||||||
|
defer os.RemoveAll(store.Root)
|
||||||
|
// Root should exist
|
||||||
|
if _, err := os.Stat(store.Root); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// List() should be empty
|
||||||
|
if l := store.List(); len(l) != 0 {
|
||||||
|
t.Fatalf("List() should return %d, not %d", 0, len(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddLayer(t *testing.T) {
|
||||||
|
store := tempStore(t)
|
||||||
|
defer os.RemoveAll(store.Root)
|
||||||
|
layer, err := store.AddLayer("foo", testArchive(t), os.Stderr, Uncompressed)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Layer path should exist
|
||||||
|
if _, err := os.Stat(layer); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// List() should return 1 layer
|
||||||
|
if l := store.List(); len(l) != 1 {
|
||||||
|
t.Fatalf("List() should return %d elements, not %d", 1, len(l))
|
||||||
|
}
|
||||||
|
// Get("foo") should return the correct layer
|
||||||
|
if foo := store.Get("foo"); foo != layer {
|
||||||
|
t.Fatalf("get(\"foo\") should return '%d', not '%d'", layer, foo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddLayerDuplicate(t *testing.T) {
|
||||||
|
store := tempStore(t)
|
||||||
|
defer os.RemoveAll(store.Root)
|
||||||
|
if _, err := store.AddLayer("foobar123", testArchive(t), os.Stderr, Uncompressed); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := store.AddLayer("foobar123", testArchive(t), os.Stderr, Uncompressed); err == nil {
|
||||||
|
t.Fatalf("Creating duplicate layer should fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HELPER FUNCTIONS
|
||||||
|
*/
|
||||||
|
|
||||||
|
func tempStore(t *testing.T) *LayerStore {
|
||||||
|
tmp, err := ioutil.TempDir("", "docker-fs-layerstore-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
store, err := NewLayerStore(tmp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
func testArchive(t *testing.T) Archive {
|
||||||
|
archive, err := fake.FakeTar()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return archive
|
||||||
|
}
|
14
fs/store.go
14
fs/store.go
|
@ -14,6 +14,7 @@ type Store struct {
|
||||||
Root string
|
Root string
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
orm *gorp.DbMap
|
orm *gorp.DbMap
|
||||||
|
layers *LayerStore
|
||||||
}
|
}
|
||||||
|
|
||||||
type Archive io.Reader
|
type Archive io.Reader
|
||||||
|
@ -33,10 +34,15 @@ func New(root string) (*Store, error) {
|
||||||
if err := orm.CreateTables(); err != nil {
|
if err := orm.CreateTables(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
layers, err := NewLayerStore(path.Join(root, "layers"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &Store{
|
return &Store{
|
||||||
Root: root,
|
Root: root,
|
||||||
db: db,
|
db: db,
|
||||||
orm: orm,
|
orm: orm,
|
||||||
|
layers: layers,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,13 +94,19 @@ func (store *Store) Get(id string) (*Image, error) {
|
||||||
return img.(*Image), err
|
return img.(*Image), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *Store) Create(layer Archive, parent *Image, pth, comment string) (*Image, error) {
|
func (store *Store) Create(layerData Archive, parent *Image, pth, comment string) (*Image, error) {
|
||||||
// FIXME: actually do something with the layer...
|
// FIXME: actually do something with the layer...
|
||||||
img := &Image{
|
img := &Image{
|
||||||
Id : future.RandomId(),
|
Id : future.RandomId(),
|
||||||
Comment: comment,
|
Comment: comment,
|
||||||
store: store,
|
store: store,
|
||||||
}
|
}
|
||||||
|
// FIXME: we shouldn't have to pass os.Stderr to AddLayer()...
|
||||||
|
// FIXME: Archive should contain compression info. For now we only support uncompressed.
|
||||||
|
_, err := store.layers.AddLayer(img.Id, layerData, os.Stderr, Uncompressed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
path := &Path{
|
path := &Path{
|
||||||
Path: path.Clean(pth),
|
Path: path.Clean(pth),
|
||||||
Image: img.Id,
|
Image: img.Id,
|
||||||
|
|
Loading…
Add table
Reference in a new issue