mirror of https://github.com/docker/docs.git
Implementing mounting/unmounting of images. Currently missing Mounpoint#Deregister
This commit is contained in:
parent
04bcdf9030
commit
b4ea31e1de
|
@ -1,4 +1,4 @@
|
||||||
package docker
|
package fs
|
||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package docker
|
package fs
|
||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
|
||||||
|
|
220
fs/store.go
220
fs/store.go
|
@ -2,21 +2,24 @@ package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
|
||||||
"github.com/coopernurse/gorp"
|
|
||||||
"os"
|
|
||||||
"io"
|
|
||||||
"path"
|
|
||||||
"github.com/dotcloud/docker/future"
|
|
||||||
"fmt"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/coopernurse/gorp"
|
||||||
|
"github.com/dotcloud/docker/future"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
Root string
|
Root string
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
orm *gorp.DbMap
|
orm *gorp.DbMap
|
||||||
layers *LayerStore
|
layers *LayerStore
|
||||||
}
|
}
|
||||||
|
|
||||||
type Archive io.Reader
|
type Archive io.Reader
|
||||||
|
@ -42,14 +45,14 @@ func New(root string) (*Store, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Store{
|
return &Store{
|
||||||
Root: root,
|
Root: root,
|
||||||
db: db,
|
db: db,
|
||||||
orm: orm,
|
orm: orm,
|
||||||
layers: layers,
|
layers: layers,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *Store) imageList(src []interface{}) ([]*Image) {
|
func (store *Store) imageList(src []interface{}) []*Image {
|
||||||
var images []*Image
|
var images []*Image
|
||||||
for _, i := range src {
|
for _, i := range src {
|
||||||
img := i.(*Image)
|
img := i.(*Image)
|
||||||
|
@ -60,7 +63,7 @@ func (store *Store) imageList(src []interface{}) ([]*Image) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *Store) Images() ([]*Image, error) {
|
func (store *Store) Images() ([]*Image, error) {
|
||||||
images , err := store.orm.Select(Image{}, "select * from images")
|
images, err := store.orm.Select(Image{}, "select * from images")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -94,15 +97,18 @@ func (store *Store) List(pth string) ([]*Image, error) {
|
||||||
|
|
||||||
func (store *Store) Get(id string) (*Image, error) {
|
func (store *Store) Get(id string) (*Image, error) {
|
||||||
img, err := store.orm.Get(Image{}, id)
|
img, err := store.orm.Get(Image{}, id)
|
||||||
|
if img == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return img.(*Image), err
|
return img.(*Image), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *Store) Create(layerData 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: we shouldn't have to pass os.Stderr to AddLayer()...
|
||||||
// FIXME: Archive should contain compression info. For now we only support uncompressed.
|
// FIXME: Archive should contain compression info. For now we only support uncompressed.
|
||||||
|
@ -111,8 +117,8 @@ func (store *Store) Create(layerData Archive, parent *Image, pth, comment string
|
||||||
return nil, errors.New(fmt.Sprintf("Could not add layer: %s", err))
|
return nil, errors.New(fmt.Sprintf("Could not add layer: %s", err))
|
||||||
}
|
}
|
||||||
path := &Path{
|
path := &Path{
|
||||||
Path: path.Clean(pth),
|
Path: path.Clean(pth),
|
||||||
Image: img.Id,
|
Image: img.Id,
|
||||||
}
|
}
|
||||||
trans, err := store.orm.Begin()
|
trans, err := store.orm.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -142,14 +148,11 @@ func (store *Store) Register(image *Image, pth string) error {
|
||||||
return trans.Commit()
|
return trans.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type Image struct {
|
type Image struct {
|
||||||
Id string
|
Id string
|
||||||
Parent string
|
Parent string
|
||||||
Comment string
|
Comment string
|
||||||
store *Store `db:"-"`
|
store *Store `db:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,19 +164,41 @@ func (image *Image) Copy(pth string) (*Image, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mountpoint struct {
|
type Mountpoint struct {
|
||||||
Image string
|
Image string
|
||||||
Root string
|
Root string
|
||||||
Rw string
|
Rw string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (image *Image) Mountpoint(root, rw string) (*Mountpoint, error) {
|
func (image *Image) Mountpoint(root, rw string) (*Mountpoint, error) {
|
||||||
mountpoint := &Mountpoint{Root: path.Clean(root), Rw: path.Clean(rw), Image: image.Id}
|
mountpoint := &Mountpoint{
|
||||||
|
Root: path.Clean(root),
|
||||||
|
Rw: path.Clean(rw),
|
||||||
|
Image: image.Id,
|
||||||
|
}
|
||||||
if err := image.store.orm.Insert(mountpoint); err != nil {
|
if err := image.store.orm.Insert(mountpoint); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return mountpoint, nil
|
return mountpoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (image *Image) layers() ([]string, error) {
|
||||||
|
var list []string
|
||||||
|
var err error
|
||||||
|
currentImg := image
|
||||||
|
for currentImg != nil {
|
||||||
|
if layer := image.store.layers.Get(image.Id); layer != "" {
|
||||||
|
list = append(list, layer)
|
||||||
|
} else {
|
||||||
|
return list, fmt.Errorf("Layer not found for image %s", image.Id)
|
||||||
|
}
|
||||||
|
currentImg, err = currentImg.store.Get(currentImg.Parent)
|
||||||
|
if err != nil {
|
||||||
|
return list, fmt.Errorf("Error while getting parent image: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (image *Image) Mountpoints() ([]*Mountpoint, error) {
|
func (image *Image) Mountpoints() ([]*Mountpoint, error) {
|
||||||
var mountpoints []*Mountpoint
|
var mountpoints []*Mountpoint
|
||||||
res, err := image.store.orm.Select(Mountpoint{}, "select * from mountpoints where Image=?", image.Id)
|
res, err := image.store.orm.Select(Mountpoint{}, "select * from mountpoints where Image=?", image.Id)
|
||||||
|
@ -186,6 +211,123 @@ func (image *Image) Mountpoints() ([]*Mountpoint, error) {
|
||||||
return mountpoints, nil
|
return mountpoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (image *Image) Mount(root, rw string) (*Mountpoint, error) {
|
||||||
|
var mountpoint *Mountpoint
|
||||||
|
if mp, err := image.fetchMountpoint(root, rw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if mp == nil {
|
||||||
|
mountpoint, err = image.Mountpoint(root, rw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not create mountpoint: %s", err)
|
||||||
|
} else if mountpoint == nil {
|
||||||
|
return nil, errors.New("No mountpoint created")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mountpoint = mp
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mountpoint.createFolders(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Now mount the layers
|
||||||
|
rwBranch := fmt.Sprintf("%v=rw", mountpoint.Rw)
|
||||||
|
roBranches := ""
|
||||||
|
layers, err := image.layers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, layer := range layers {
|
||||||
|
roBranches += fmt.Sprintf("%v=ro:", layer)
|
||||||
|
}
|
||||||
|
branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
|
||||||
|
if err := mount("none", mountpoint.Root, "aufs", 0, branches); err != nil {
|
||||||
|
return mountpoint, err
|
||||||
|
}
|
||||||
|
if !mountpoint.Mounted() {
|
||||||
|
return mountpoint, errors.New("Mount failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return mountpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *Mountpoint) createFolders() error {
|
||||||
|
if err := os.Mkdir(mp.Root, 0755); err != nil && !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.Mkdir(mp.Rw, 0755); err != nil && !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *Mountpoint) Mounted() bool {
|
||||||
|
root, err := os.Stat(mp.Root)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
parent, err := os.Stat(filepath.Join(mp.Root, ".."))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootSt := root.Sys().(*syscall.Stat_t)
|
||||||
|
parentSt := parent.Sys().(*syscall.Stat_t)
|
||||||
|
return rootSt.Dev != parentSt.Dev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *Mountpoint) Umount() error {
|
||||||
|
if !mp.Mounted() {
|
||||||
|
return errors.New("Mountpoint doesn't seem to be mounted")
|
||||||
|
}
|
||||||
|
if err := syscall.Unmount(mp.Root, 0); err != nil {
|
||||||
|
return fmt.Errorf("Unmount syscall failed: %v", err)
|
||||||
|
}
|
||||||
|
if mp.Mounted() {
|
||||||
|
return fmt.Errorf("Umount: Filesystem still mounted after calling umount(%v)", mp.Root)
|
||||||
|
}
|
||||||
|
// Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint
|
||||||
|
// for some time. We'll just keep retrying until it succeeds.
|
||||||
|
for retries := 0; retries < 1000; retries++ {
|
||||||
|
err := os.Remove(mp.Root)
|
||||||
|
if err == nil {
|
||||||
|
// rm mntpoint succeeded
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// mntpoint doesn't exist anymore. Success.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// fmt.Printf("(%v) Remove %v returned: %v\n", retries, mp.Root, err)
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Umount: Failed to umount %v", mp.Root)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *Mountpoint) Deregister() error {
|
||||||
|
if mp.Mounted() {
|
||||||
|
return errors.New("Mountpoint is currently mounted, can't deregister")
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (image *Image) fetchMountpoint(root, rw string) (*Mountpoint, error) {
|
||||||
|
res, err := image.store.orm.Select(Mountpoint{}, "select * from mountpoints where Image=? and Root=? and Rw=?", image.Id, root, rw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(res) < 1 || res[0] == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return res[0].(*Mountpoint), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (store *Store) AddTag(imageId, tagName string) error {
|
func (store *Store) AddTag(imageId, tagName string) error {
|
||||||
if image, err := store.Get(imageId); err != nil {
|
if image, err := store.Get(imageId); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -194,8 +336,8 @@ func (store *Store) AddTag(imageId, tagName string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
err2 := store.orm.Insert(&Tag{
|
err2 := store.orm.Insert(&Tag{
|
||||||
TagName: tagName,
|
TagName: tagName,
|
||||||
Image: imageId,
|
Image: imageId,
|
||||||
})
|
})
|
||||||
|
|
||||||
return err2
|
return err2
|
||||||
|
@ -222,11 +364,11 @@ func (store *Store) GetByTag(tagName string) (*Image, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Path struct {
|
type Path struct {
|
||||||
Path string
|
Path string
|
||||||
Image string
|
Image string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
TagName string
|
TagName string
|
||||||
Image string
|
Image string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"io/ioutil"
|
|
||||||
"github.com/dotcloud/docker/fake"
|
|
||||||
"os"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/fake"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
|
@ -165,7 +165,7 @@ func TestMountPoint(t *testing.T) {
|
||||||
if mountpoint.Root != "/tmp/a" {
|
if mountpoint.Root != "/tmp/a" {
|
||||||
t.Fatal("Wrong mountpoint root (should be %s, not %s)", "/tmp/a", mountpoint.Root)
|
t.Fatal("Wrong mountpoint root (should be %s, not %s)", "/tmp/a", mountpoint.Root)
|
||||||
}
|
}
|
||||||
if mountpoint.Rw!= "/tmp/b" {
|
if mountpoint.Rw != "/tmp/b" {
|
||||||
t.Fatal("Wrong mountpoint root (should be %s, not %s)", "/tmp/b", mountpoint.Rw)
|
t.Fatal("Wrong mountpoint root (should be %s, not %s)", "/tmp/b", mountpoint.Rw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,9 +193,8 @@ func TestMountpointDuplicateRoot(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func TestMount(t *testing.T) {
|
func TestMount(t *testing.T) {
|
||||||
store, err := TempStore()
|
store, err := TempStore("test-mount")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -227,8 +226,10 @@ func TestMount(t *testing.T) {
|
||||||
t.Fatal("Mountpoint not mounted")
|
t.Fatal("Mountpoint not mounted")
|
||||||
}
|
}
|
||||||
// There should be one mountpoint registered
|
// There should be one mountpoint registered
|
||||||
if l := len(image.Mountpoints()); l != 1 {
|
if mps, err := image.Mountpoints(); err != nil {
|
||||||
t.Fatal("Wrong number of mountpoints registered (should be %d, not %d)", 1, l)
|
t.Fatal(err)
|
||||||
|
} else if len(mps) != 1 {
|
||||||
|
t.Fatal("Wrong number of mountpoints registered (should be %d, not %d)", 1, len(mps))
|
||||||
}
|
}
|
||||||
// Unmounting should work
|
// Unmounting should work
|
||||||
if err := mountpoint.Umount(); err != nil {
|
if err := mountpoint.Umount(); err != nil {
|
||||||
|
@ -238,18 +239,19 @@ func TestMount(t *testing.T) {
|
||||||
if err := mountpoint.Deregister(); err != nil {
|
if err := mountpoint.Deregister(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if l := len(image.Mountpoints()); l != 0 {
|
if mps, err := image.Mountpoints(); err != nil {
|
||||||
t.Fatal("Wrong number of mountpoints registered (should be %d, not %d)", 0, l)
|
t.Fatal(err)
|
||||||
|
} else if len(mps) != 0 {
|
||||||
|
t.Fatal("Wrong number of mountpoints registered (should be %d, not %d)", 0, len(mps))
|
||||||
}
|
}
|
||||||
// General health check
|
// General health check
|
||||||
if err := healthCheck(); err != nil {
|
if err := healthCheck(store); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
func TempStore(prefix string) (*Store, error) {
|
func TempStore(prefix string) (*Store, error) {
|
||||||
dir, err := ioutil.TempDir("", "docker-fs-test-" + prefix)
|
dir, err := ioutil.TempDir("", "docker-fs-test-"+prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -294,4 +296,3 @@ func healthCheck(store *Store) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue