diff --git a/fs/changes.go b/fs/changes.go deleted file mode 100644 index 659f688c45..0000000000 --- a/fs/changes.go +++ /dev/null @@ -1,127 +0,0 @@ -package fs - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -type ChangeType int - -const ( - ChangeModify = iota - ChangeAdd - ChangeDelete -) - -type Change struct { - Path string - Kind ChangeType -} - -func (change *Change) String() string { - var kind string - switch change.Kind { - case ChangeModify: - kind = "C" - case ChangeAdd: - kind = "A" - case ChangeDelete: - kind = "D" - } - return fmt.Sprintf("%s %s", kind, change.Path) -} - -func (store *Store) Changes(mp *Mountpoint) ([]Change, error) { - var changes []Change - image, err := store.Get(mp.Image) - if err != nil { - return nil, err - } - layers, err := image.layers() - if err != nil { - return nil, err - } - - err = filepath.Walk(mp.Rw, func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } - - // Rebase path - path, err = filepath.Rel(mp.Rw, path) - if err != nil { - return err - } - path = filepath.Join("/", path) - - // Skip root - if path == "/" { - return nil - } - - // Skip AUFS metadata - if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched { - return err - } - - change := Change{ - Path: path, - } - - // Find out what kind of modification happened - file := filepath.Base(path) - // If there is a whiteout, then the file was removed - if strings.HasPrefix(file, ".wh.") { - originalFile := strings.TrimLeft(file, ".wh.") - change.Path = filepath.Join(filepath.Dir(path), originalFile) - change.Kind = ChangeDelete - } else { - // Otherwise, the file was added - change.Kind = ChangeAdd - - // ...Unless it already existed in a top layer, in which case, it's a modification - for _, layer := range layers { - stat, err := os.Stat(filepath.Join(layer, path)) - if err != nil && !os.IsNotExist(err) { - return err - } - if err == nil { - // The file existed in the top layer, so that's a modification - - // However, if it's a directory, maybe it wasn't actually modified. - // If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar - if stat.IsDir() && f.IsDir() { - if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() { - // Both directories are the same, don't record the change - return nil - } - } - change.Kind = ChangeModify - break - } - } - } - - // Record change - changes = append(changes, change) - return nil - }) - if err != nil { - return nil, err - } - return changes, nil -} - -// Reset removes all changes to the filesystem, reverting it to its initial state. -func (mp *Mountpoint) Reset() error { - if err := os.RemoveAll(mp.Rw); err != nil { - return err - } - // We removed the RW directory itself along with its content: let's re-create an empty one. - if err := mp.createFolders(); err != nil { - return err - } - return nil -} diff --git a/fs/layers.go b/fs/layers.go deleted file mode 100644 index dc7e621f85..0000000000 --- a/fs/layers.go +++ /dev/null @@ -1,113 +0,0 @@ -package fs - -import ( - "errors" - "fmt" - "github.com/dotcloud/docker/future" - "io/ioutil" - "os" - "path" - "path/filepath" -) - -type LayerStore struct { - Root string -} - -func NewLayerStore(root string) (*LayerStore, error) { - abspath, err := filepath.Abs(root) - if err != nil { - 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{ - Root: abspath, - }, nil -} - -func (store *LayerStore) List() []string { - files, err := ioutil.ReadDir(store.Root) - if err != nil { - return []string{} - } - var layers []string - for _, st := range files { - if st.IsDir() { - layers = append(layers, path.Join(store.Root, st.Name())) - } - } - return layers -} - -func (store *LayerStore) Get(id string) string { - if !store.Exists(id) { - return "" - } - return store.layerPath(id) -} - -func (store *LayerStore) rootExists() (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 *LayerStore) Init() error { - if exists, err := store.rootExists(); err != nil { - return err - } else if exists { - return nil - } - return os.Mkdir(store.Root, 0700) -} - -func (store *LayerStore) Mktemp() (string, error) { - tmpName := future.RandomId() - tmpPath := path.Join(store.Root, "tmp-"+tmpName) - if err := os.Mkdir(tmpPath, 0700); err != nil { - return "", err - } - return tmpPath, nil -} - -func (store *LayerStore) layerPath(id string) string { - return path.Join(store.Root, id) -} - -func (store *LayerStore) AddLayer(id string, archive Archive) (string, error) { - if _, err := os.Stat(store.layerPath(id)); err == nil { - return "", fmt.Errorf("Layer already exists: %v", id) - } - tmp, err := store.Mktemp() - defer os.RemoveAll(tmp) - if err != nil { - return "", fmt.Errorf("Mktemp failed: %s", err) - } - if err := Untar(archive, tmp); err != nil { - return "", err - } - layer := store.layerPath(id) - if !store.Exists(id) { - if err := os.Rename(tmp, layer); err != nil { - return "", fmt.Errorf("Could not rename temp dir to layer %s: %s", layer, err) - } - } - return layer, nil -} - -func (store *LayerStore) Exists(id string) bool { - st, err := os.Stat(store.layerPath(id)) - if err != nil { - return false - } - return st.IsDir() -} diff --git a/fs/layers_test.go b/fs/layers_test.go deleted file mode 100644 index ee7e59fd23..0000000000 --- a/fs/layers_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package fs - -import ( - "archive/tar" - "bytes" - "io" - "io/ioutil" - "os" - "testing" -) - -func fakeTar() (io.Reader, error) { - content := []byte("Hello world!\n") - buf := new(bytes.Buffer) - tw := tar.NewWriter(buf) - for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} { - hdr := new(tar.Header) - hdr.Size = int64(len(content)) - hdr.Name = name - if err := tw.WriteHeader(hdr); err != nil { - return nil, err - } - tw.Write([]byte(content)) - } - tw.Close() - return buf, nil -} - -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)) - 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)); err != nil { - t.Fatal(err) - } - if _, err := store.AddLayer("foobar123", testArchive(t)); 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 := fakeTar() - if err != nil { - t.Fatal(err) - } - return archive -} diff --git a/fs/remove_test.go b/fs/remove_test.go deleted file mode 100644 index 81a234decf..0000000000 --- a/fs/remove_test.go +++ /dev/null @@ -1,222 +0,0 @@ -package fs - -import ( - "fmt" - "testing" -) - -func countImages(store *Store) int { - paths, err := store.Images() - if err != nil { - panic(err) - } - return len(paths) -} - -func TestRemoveInPath(t *testing.T) { - store, err := TempStore("test-remove-in-path") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } - - // Test 10 create / Delete all - for i := 0; i < 10; i++ { - if _, err := store.Create(archive, nil, "foo", "Testing"); err != nil { - t.Fatal(err) - } - } - if c := countImages(store); c != 10 { - t.Fatalf("Expected 10 images, %d found", c) - } - if err := store.RemoveInPath("foo"); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } - - // Test 10 create / Delete 1 - for i := 0; i < 10; i++ { - if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil { - t.Fatal(err) - } - } - if c := countImages(store); c != 10 { - t.Fatalf("Expected 10 images, %d found", c) - } - if err := store.RemoveInPath("foo-0"); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 9 { - t.Fatalf("Expected 9 images, %d found", c) - } - - // Delete failure - if err := store.RemoveInPath("Not_Foo"); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 9 { - t.Fatalf("Expected 9 images, %d found", c) - } -} - -func TestRemove(t *testing.T) { - store, err := TempStore("test-remove") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } - - // Test 1 create / 1 delete - img, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 1 { - t.Fatalf("Expected 1 images, %d found", c) - } - if err := store.Remove(img); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } - - // Test 2 create (same name) / 1 delete - img1, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - img2, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 2 { - t.Fatalf("Expected 2 images, %d found", c) - } - if err := store.Remove(img1); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 1 { - t.Fatalf("Expected 1 images, %d found", c) - } - - // Test delete wrong name - // Note: If we change orm and Delete of non existing return error, we will need to change this test - if err := store.Remove(&Image{Id: "Not_foo", store: img2.store}); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 1 { - t.Fatalf("Expected 1 images, %d found", c) - } - - // Test delete last one - if err := store.Remove(img2); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } -} - -func TestRemoveRegexp(t *testing.T) { - store, err := TempStore("test-remove-regexp") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } - - // Test 10 create with different names / Delete all good regexp - for i := 0; i < 10; i++ { - if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil { - t.Fatal(err) - } - } - if c := countImages(store); c != 10 { - t.Fatalf("Expected 10 images, %d found", c) - } - if err := store.RemoveRegexp("foo"); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } - - // Test 10 create with different names / Delete all good regexp globing - for i := 0; i < 10; i++ { - if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil { - t.Fatal(err) - } - } - if c := countImages(store); c != 10 { - t.Fatalf("Expected 10 images, %d found", c) - } - if err := store.RemoveRegexp("foo-*"); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } - - // Test 10 create with different names / Delete all bad regexp - for i := 0; i < 10; i++ { - if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil { - t.Fatal(err) - } - } - if c := countImages(store); c != 10 { - t.Fatalf("Expected 10 images, %d found", c) - } - if err := store.RemoveRegexp("oo-*"); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 0 { - t.Fatalf("Expected 0 images, %d found", c) - } - - // Test 10 create with different names / Delete none strict regexp - for i := 0; i < 10; i++ { - if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil { - t.Fatal(err) - } - } - if c := countImages(store); c != 10 { - t.Fatalf("Expected 10 images, %d found", c) - } - if err := store.RemoveRegexp("^oo-"); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 10 { - t.Fatalf("Expected 10 images, %d found", c) - } - - // Test delete 2 - if err := store.RemoveRegexp("^foo-[1,2]$"); err != nil { - t.Fatal(err) - } - if c := countImages(store); c != 8 { - t.Fatalf("Expected 8 images, %d found", c) - } -} diff --git a/fs/store.go b/fs/store.go deleted file mode 100644 index d7fcb35421..0000000000 --- a/fs/store.go +++ /dev/null @@ -1,521 +0,0 @@ -package fs - -import ( - "database/sql" - "fmt" - "github.com/dotcloud/docker/future" - _ "github.com/mattn/go-sqlite3" - "github.com/shykes/gorp" //Forked to implement CreateTablesOpts - "io" - "io/ioutil" - "os" - "path" - "path/filepath" - "regexp" - "strings" - "syscall" - "time" -) - -type Store struct { - Root string - db *sql.DB - orm *gorp.DbMap - layers *LayerStore -} - -type Archive io.Reader - -func New(root string) (*Store, error) { - isNewStore := true - - if err := os.Mkdir(root, 0700); err != nil && !os.IsExist(err) { - return nil, err - } - db, err := sql.Open("sqlite3", path.Join(root, "db")) - if err != nil { - return nil, err - } - orm := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}} - orm.AddTableWithName(Image{}, "images").SetKeys(false, "Id") - orm.AddTableWithName(Path{}, "paths").SetKeys(false, "Path", "Image") - orm.AddTableWithName(Mountpoint{}, "mountpoints").SetKeys(false, "Root") - orm.AddTableWithName(Tag{}, "tags").SetKeys(false, "TagName") - if isNewStore { - if err := orm.CreateTablesOpts(true); err != nil { - return nil, err - } - } - - layers, err := NewLayerStore(path.Join(root, "layers")) - if err != nil { - return nil, err - } - return &Store{ - Root: root, - db: db, - orm: orm, - layers: layers, - }, nil -} - -func (store *Store) imageList(src []interface{}) []*Image { - var images []*Image - for _, i := range src { - img := i.(*Image) - img.store = store - images = append(images, img) - } - return images -} - -func (store *Store) Images() ([]*Image, error) { - images, err := store.orm.Select(Image{}, "select * from images") - if err != nil { - return nil, err - } - return store.imageList(images), nil -} - -func (store *Store) Paths() ([]string, error) { - var paths []string - rows, err := store.db.Query("select distinct Path from paths order by Path") - if err != nil { - return nil, err - } - for rows.Next() { - var path string - if err := rows.Scan(&path); err != nil { - return nil, err - } - paths = append(paths, path) - } - return paths, nil -} - -func (store *Store) RemoveInPath(pth string) error { - images, err := store.List(pth) - if err != nil { - return err - } - for _, img := range images { - if err = store.Remove(img); err != nil { - return err - } - } - return nil -} - -// DeleteMatch deletes all images whose name matches `pattern` -func (store *Store) RemoveRegexp(pattern string) error { - // Retrieve all the paths - paths, err := store.Paths() - if err != nil { - return err - } - // Check the pattern on each elements - for _, pth := range paths { - if match, err := regexp.MatchString(pattern, pth); err != nil { - return err - } else if match { - // If there is a match, remove it - if err := store.RemoveInPath(pth); err != nil { - return nil - } - } - } - return nil -} - -func (store *Store) Remove(img *Image) error { - _, err := store.orm.Delete(img) - return err -} - -func (store *Store) List(pth string) ([]*Image, error) { - pth = path.Clean(pth) - images, err := store.orm.Select(Image{}, "select images.* from images, paths where Path=? and paths.Image=images.Id order by images.Created desc", pth) - if err != nil { - return nil, err - } - return store.imageList(images), nil -} - -func (store *Store) Find(pth string) (*Image, error) { - pth = path.Clean(pth) - img, err := store.Get(pth) - if err != nil { - return nil, err - } else if img != nil { - return img, nil - } - - var q string - var args []interface{} - // FIXME: this breaks if the path contains a ':' - // If format is path:rev - if parts := strings.SplitN(pth, ":", 2); len(parts) == 2 { - q = "select Images.* from images, paths where Path=? and images.Id=? and paths.Image=images.Id" - args = []interface{}{parts[0], parts[1]} - // If format is path:rev - } else { - q = "select images.* from images, paths where Path=? and paths.Image=images.Id order by images.Created desc limit 1" - args = []interface{}{parts[0]} - } - images, err := store.orm.Select(Image{}, q, args...) - if err != nil { - return nil, err - } else if len(images) < 1 { - return nil, nil - } - img = images[0].(*Image) - img.store = store - return img, nil -} - -func (store *Store) Get(id string) (*Image, error) { - img, err := store.orm.Get(Image{}, id) - if img == nil { - return nil, err - } - res := img.(*Image) - res.store = store - return res, err -} - -func (store *Store) Create(layerData Archive, parent *Image, pth, comment string) (*Image, error) { - // FIXME: actually do something with the layer... - img := &Image{ - Id: future.RandomId(), - Comment: comment, - Created: time.Now().Unix(), - store: store, - } - if parent != nil { - img.Parent = parent.Id - } - // FIXME: Archive should contain compression info. For now we only support uncompressed. - err := store.Register(layerData, img, pth) - return img, err -} - -func (store *Store) Register(layerData Archive, img *Image, pth string) error { - img.store = store - _, err := store.layers.AddLayer(img.Id, layerData) - if err != nil { - return fmt.Errorf("Could not add layer: %s", err) - } - pathObj := &Path{ - Path: path.Clean(pth), - Image: img.Id, - } - trans, err := store.orm.Begin() - if err != nil { - return fmt.Errorf("Could not begin transaction: %s", err) - } - if err := trans.Insert(img); err != nil { - return fmt.Errorf("Could not insert image info: %s", err) - } - if err := trans.Insert(pathObj); err != nil { - return fmt.Errorf("Could not insert path info: %s", err) - } - if err := trans.Commit(); err != nil { - return fmt.Errorf("Could not commit transaction: %s", err) - } - return nil -} - -func (store *Store) Layers() []string { - return store.layers.List() -} - -type Image struct { - Id string - Parent string - Comment string - Created int64 - store *Store `db:"-"` -} - -func (image *Image) Copy(pth string) (*Image, error) { - if err := image.store.orm.Insert(&Path{Path: pth, Image: image.Id}); err != nil { - return nil, err - } - return image, nil -} - -type Mountpoint struct { - Image string - Root string - Rw string - Store *Store `db:"-"` -} - -func (image *Image) Mountpoint(root, rw string) (*Mountpoint, error) { - mountpoint := &Mountpoint{ - Root: path.Clean(root), - Rw: path.Clean(rw), - Image: image.Id, - Store: image.store, - } - if err := image.store.orm.Insert(mountpoint); err != nil { - return nil, err - } - 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(currentImg.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) - } - } - if len(list) == 0 { - return nil, fmt.Errorf("No layer found for image %s\n", image.Id) - } - return list, nil -} - -func (image *Image) Mountpoints() ([]*Mountpoint, error) { - var mountpoints []*Mountpoint - res, err := image.store.orm.Select(Mountpoint{}, "select * from mountpoints where Image=?", image.Id) - if err != nil { - return nil, err - } - for _, mp := range res { - mountpoints = append(mountpoints, mp.(*Mountpoint)) - } - return mountpoints, nil -} - -func (image *Image) Mount(root, rw string) (*Mountpoint, error) { - var mountpoint *Mountpoint - if mp, err := image.store.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, fmt.Errorf("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, fmt.Errorf("Mount failed") - } - - // FIXME: Create tests for deletion - // FIXME: move this part to change.go, maybe refactor - // fs.Change() to avoid the fake mountpoint - // Retrieve the changeset from the parent and apply it to the container - // - Retrieve the changes - changes, err := image.store.Changes(&Mountpoint{ - Image: image.Id, - Root: layers[0], - Rw: layers[0], - Store: image.store}) - if err != nil { - return nil, err - } - // Iterate on changes - for _, c := range changes { - // If there is a delete - if c.Kind == ChangeDelete { - // Make sure the directory exists - file_path, file_name := path.Dir(c.Path), path.Base(c.Path) - if err := os.MkdirAll(path.Join(mountpoint.Rw, file_path), 0755); err != nil { - return nil, err - } - // And create the whiteout (we just need to create empty file, discard the return) - if _, err := os.Create(path.Join(path.Join(mountpoint.Rw, file_path), - ".wh."+path.Base(file_name))); err != nil { - return nil, err - } - } - } - return mountpoint, nil -} - -func (mp *Mountpoint) EnsureMounted() error { - if mp.Mounted() { - return nil - } - img, err := mp.Store.Get(mp.Image) - if err != nil { - return err - } - - _, err = img.Mount(mp.Root, mp.Rw) - return err -} - -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 fmt.Errorf("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 fmt.Errorf("Mountpoint is currently mounted, can't deregister") - } - - _, err := mp.Store.orm.Delete(mp) - return err -} - -func (store *Store) FetchMountpoint(root, rw string) (*Mountpoint, error) { - res, err := store.orm.Select(Mountpoint{}, "select * from mountpoints where Root=? and Rw=?", root, rw) - if err != nil { - return nil, err - } else if len(res) < 1 || res[0] == nil { - return nil, nil - } - - mp := res[0].(*Mountpoint) - mp.Store = store - return mp, nil -} - -// OpenFile opens the named file for reading. -func (mp *Mountpoint) OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) { - if err := mp.EnsureMounted(); err != nil { - return nil, err - } - return os.OpenFile(filepath.Join(mp.Root, path), flag, perm) -} - -// ReadDir reads the directory named by dirname, relative to the Mountpoint's root, -// and returns a list of sorted directory entries -func (mp *Mountpoint) ReadDir(dirname string) ([]os.FileInfo, error) { - if err := mp.EnsureMounted(); err != nil { - return nil, err - } - return ioutil.ReadDir(filepath.Join(mp.Root, dirname)) -} - -func (store *Store) AddTag(imageId, tagName string) error { - if image, err := store.Get(imageId); err != nil { - return err - } else if image == nil { - return fmt.Errorf("No image with ID %s", imageId) - } - - err2 := store.orm.Insert(&Tag{ - TagName: tagName, - Image: imageId, - }) - - return err2 -} - -func (store *Store) GetByTag(tagName string) (*Image, error) { - res, err := store.orm.Get(Tag{}, tagName) - if err != nil { - return nil, err - } else if res == nil { - return nil, fmt.Errorf("No image associated to tag \"%s\"", tagName) - } - - tag := res.(*Tag) - - img, err2 := store.Get(tag.Image) - if err2 != nil { - return nil, err2 - } else if img == nil { - return nil, fmt.Errorf("Tag was found but image seems to be inexistent.") - } - - return img, nil -} - -type Path struct { - Path string - Image string -} - -type Tag struct { - TagName string - Image string -} diff --git a/fs/store_test.go b/fs/store_test.go deleted file mode 100644 index 8413ea0812..0000000000 --- a/fs/store_test.go +++ /dev/null @@ -1,277 +0,0 @@ -package fs - -import ( - "fmt" - "github.com/dotcloud/docker/future" - "io/ioutil" - "os" - "testing" - "time" -) - -func TestInit(t *testing.T) { - store, err := TempStore("testinit") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - paths, err := store.Paths() - if err != nil { - t.Fatal(err) - } - if l := len(paths); l != 0 { - t.Fatal("Fresh store should be empty after init (len=%d)", l) - } -} - -// FIXME: Do more extensive tests (ex: create multiple, delete, recreate; -// create multiple, check the amount of images and paths, etc..) -func TestCreate(t *testing.T) { - store, err := TempStore("testcreate") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - image, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - if images, err := store.Images(); err != nil { - t.Fatal(err) - } else if l := len(images); l != 1 { - t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l) - } - if images, err := store.List("foo"); err != nil { - t.Fatal(err) - } else if l := len(images); l != 1 { - t.Fatalf("Path foo has wrong number of images (should be %d, not %d)", 1, l) - } else if images[0].Id != image.Id { - t.Fatalf("Imported image should be listed at path foo (%s != %s)", images[0], image) - } -} - -func TestRegister(t *testing.T) { - store, err := TempStore("testregister") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - image := &Image{ - Id: future.RandomId(), - Comment: "testing", - Created: time.Now().Unix(), - store: store, - } - err = store.Register(archive, image, "foo") - if err != nil { - t.Fatal(err) - } - if images, err := store.Images(); err != nil { - t.Fatal(err) - } else if l := len(images); l != 1 { - t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l) - } - if images, err := store.List("foo"); err != nil { - t.Fatal(err) - } else if l := len(images); l != 1 { - t.Fatalf("Path foo has wrong number of images (should be %d, not %d)", 1, l) - } else if images[0].Id != image.Id { - t.Fatalf("Imported image should be listed at path foo (%s != %s)", images[0], image) - } -} - -func TestTag(t *testing.T) { - store, err := TempStore("testtag") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - image, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - if images, err := store.Images(); err != nil { - t.Fatal(err) - } else if l := len(images); l != 1 { - t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l) - } - - if err := store.AddTag(image.Id, "baz"); err != nil { - t.Fatalf("Error while adding a tag to created image: %s", err) - } - - if taggedImage, err := store.GetByTag("baz"); err != nil { - t.Fatalf("Error while trying to retrieve image for tag 'baz': %s", err) - } else if taggedImage.Id != image.Id { - t.Fatalf("Expected to retrieve image %s but found %s instead", image.Id, taggedImage.Id) - } -} - -// Copy an image to a new path -func TestCopyNewPath(t *testing.T) { - store, err := TempStore("testcopynewpath") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - src, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - dst, err := src.Copy("bar") - if err != nil { - t.Fatal(err) - } - // ID should be the same - if src.Id != dst.Id { - t.Fatal("Different IDs") - } - // Check number of images at source path - if images, err := store.List("foo"); err != nil { - t.Fatal(err) - } else if l := len(images); l != 1 { - t.Fatal("Wrong number of images at source path (should be %d, not %d)", 1, l) - } - // Check number of images at destination path - if images, err := store.List("bar"); err != nil { - t.Fatal(err) - } else if l := len(images); l != 1 { - t.Fatal("Wrong number of images at destination path (should be %d, not %d)", 1, l) - } - if err := healthCheck(store); err != nil { - t.Fatal(err) - } -} - -// Copying an image to the same path twice should fail -func TestCopySameName(t *testing.T) { - store, err := TempStore("testcopysamename") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - src, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - _, err = src.Copy("foo") - if err == nil { - t.Fatal("Copying an image to the same patch twice should fail.") - } -} - -func TestMountPoint(t *testing.T) { - store, err := TempStore("test-mountpoint") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - image, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - mountpoint, err := image.Mountpoint("/tmp/a", "/tmp/b") - if err != nil { - t.Fatal(err) - } - if mountpoint.Root != "/tmp/a" { - t.Fatal("Wrong mountpoint root (should be %s, not %s)", "/tmp/a", mountpoint.Root) - } - if mountpoint.Rw != "/tmp/b" { - t.Fatal("Wrong mountpoint root (should be %s, not %s)", "/tmp/b", mountpoint.Rw) - } -} - -func TestMountpointDuplicateRoot(t *testing.T) { - store, err := TempStore("test-mountpoint") - if err != nil { - t.Fatal(err) - } - defer nuke(store) - archive, err := fakeTar() - if err != nil { - t.Fatal(err) - } - image, err := store.Create(archive, nil, "foo", "Testing") - if err != nil { - t.Fatal(err) - } - _, err = image.Mountpoint("/tmp/a", "/tmp/b") - if err != nil { - t.Fatal(err) - } - if _, err = image.Mountpoint("/tmp/a", "/tmp/foobar"); err == nil { - t.Fatal("Duplicate mountpoint root should fail") - } -} - -func TempStore(prefix string) (*Store, error) { - dir, err := ioutil.TempDir("", "docker-fs-test-"+prefix) - if err != nil { - return nil, err - } - return New(dir) -} - -func nuke(store *Store) error { - return os.RemoveAll(store.Root) -} - -// Look for inconsistencies in a store. -func healthCheck(store *Store) error { - parents := make(map[string]bool) - paths, err := store.Paths() - if err != nil { - return err - } - for _, path := range paths { - images, err := store.List(path) - if err != nil { - return err - } - IDs := make(map[string]bool) // All IDs for this path - for _, img := range images { - // Check for duplicate IDs per path - if _, exists := IDs[img.Id]; exists { - return fmt.Errorf("Duplicate ID: %s", img.Id) - } else { - IDs[img.Id] = true - } - // Store parent for 2nd pass - if parent := img.Parent; parent != "" { - parents[parent] = true - } - } - } - // Check non-existing parents - for parent := range parents { - if _, exists := parents[parent]; !exists { - return fmt.Errorf("Reference to non-registered parent: %s", parent) - } - } - return nil -}