snapshot/overlay: add snapshot test suite to overlay

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day 2017-02-02 18:55:53 -08:00
parent 68fd25221d
commit d0b4ce8d17
No known key found for this signature in database
GPG Key ID: 67B3DED84EDC823F
3 changed files with 124 additions and 31 deletions

View File

@ -2,15 +2,17 @@ package overlay
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"github.com/docker/containerd"
digest "github.com/opencontainers/go-digest"
)
func NewOverlay(root string) (*Overlay, error) {
func NewDriver(root string) (*Overlay, error) {
if err := os.MkdirAll(root, 0700); err != nil {
return nil, err
}
@ -33,31 +35,87 @@ type Overlay struct {
cache *cache
}
func (o *Overlay) Prepare(key string, parentName string) ([]containerd.Mount, error) {
if err := validKey(key); err != nil {
return nil, err
}
func (o *Overlay) Prepare(key, parent string) ([]containerd.Mount, error) {
active, err := o.newActiveDir(key)
if err != nil {
return nil, err
}
if parentName != "" {
if err := active.setParent(parentName); err != nil {
if parent != "" {
if err := active.setParent(parent); err != nil {
return nil, err
}
}
return o.Mounts(key)
}
func (o *Overlay) View(key, parent string) ([]containerd.Mount, error) {
panic("not implemented")
}
// Mounts returns the mounts for the transaction identified by key. Can be
// called on an read-write or readonly transaction.
//
// This can be used to recover mounts after calling View or Prepare.
func (o *Overlay) Mounts(key string) ([]containerd.Mount, error) {
active := o.getActive(key)
return active.mounts(o.cache)
}
func (o *Overlay) Commit(name, key string) error {
active := o.getActive(key)
return active.commit(name)
return active.commit(name, o.cache)
}
// Remove abandons the transaction identified by key. All resources
// associated with the key will be removed.
func (o *Overlay) Remove(key string) error {
panic("not implemented")
}
// Parent returns the parent of snapshot identified by name.
func (o *Overlay) Parent(name string) (string, error) {
ppath, err := o.cache.get(filepath.Join(o.root, "snapshots", hash(name)))
if err != nil {
if os.IsNotExist(err) {
return "", nil // no parent
}
return "", err
}
p, err := ioutil.ReadFile(filepath.Join(ppath, "name"))
if err != nil {
return "", err
}
return string(p), nil
}
// Exists returns true if the snapshot with name exists.
func (o *Overlay) Exists(name string) bool {
panic("not implemented")
}
// Delete the snapshot idenfitied by name.
//
// If name has children, the operation will fail.
func (o *Overlay) Delete(name string) error {
panic("not implemented")
}
// Walk the committed snapshots.
func (o *Overlay) Walk(fn func(name string) error) error {
panic("not implemented")
}
// Active will call fn for each active transaction.
func (o *Overlay) Active(fn func(key string) error) error {
panic("not implemented")
}
func (o *Overlay) newActiveDir(key string) (*activeDir, error) {
var (
hash = hash(key)
path = filepath.Join(o.root, "active", hash)
path = filepath.Join(o.root, "active", hash(key))
)
a := &activeDir{
path: path,
@ -82,15 +140,8 @@ func (o *Overlay) getActive(key string) *activeDir {
}
}
func validKey(key string) error {
_, err := filepath.Abs(key)
return err
}
func hash(k string) string {
h := md5.New()
h.Write([]byte(k))
return hex.EncodeToString(h.Sum(nil))
return digest.FromString(k).Hex()
}
type activeDir struct {
@ -103,14 +154,26 @@ func (a *activeDir) delete() error {
}
func (a *activeDir) setParent(name string) error {
return os.Symlink(filepath.Join(a.snapshotsDir, name), filepath.Join(a.path, "parent"))
return os.Symlink(filepath.Join(a.snapshotsDir, hash(name)), filepath.Join(a.path, "parent"))
}
func (a *activeDir) commit(name string) error {
func (a *activeDir) commit(name string, c *cache) error {
// TODO(stevvooe): This doesn't quite meet the current model. The new model
// is to copy all of this out and let the transaction continue. We don't
// really have tests for it yet, but this will be the spot to fix it.
//
// Nothing should be removed until remove is called on the active
// transaction.
if err := os.RemoveAll(filepath.Join(a.path, "work")); err != nil {
return err
}
return os.Rename(a.path, filepath.Join(a.snapshotsDir, name))
if err := ioutil.WriteFile(filepath.Join(a.path, "name"), []byte(name), 0644); err != nil {
return err
}
c.invalidate(a.path) // clears parent cache, since we end up moving.
return os.Rename(a.path, filepath.Join(a.snapshotsDir, hash(name)))
}
func (a *activeDir) mounts(c *cache) ([]containerd.Mount, error) {
@ -180,3 +243,10 @@ func (c *cache) get(path string) (string, error) {
}
return parentRoot, nil
}
func (c *cache) invalidate(path string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.parents, path)
}

View File

@ -8,7 +8,8 @@ import (
"testing"
"github.com/docker/containerd"
"github.com/docker/containerd/snapshot/testutil"
"github.com/docker/containerd/snapshot"
"github.com/docker/containerd/testutil"
)
func TestOverlay(t *testing.T) {
@ -17,7 +18,7 @@ func TestOverlay(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(root)
o, err := NewOverlay(root)
o, err := NewDriver(root)
if err != nil {
t.Error(err)
return
@ -52,7 +53,7 @@ func TestOverlayCommit(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(root)
o, err := NewOverlay(root)
o, err := NewDriver(root)
if err != nil {
t.Error(err)
return
@ -80,7 +81,7 @@ func TestOverlayOverlayMount(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(root)
o, err := NewOverlay(root)
o, err := NewDriver(root)
if err != nil {
t.Error(err)
return
@ -110,10 +111,11 @@ func TestOverlayOverlayMount(t *testing.T) {
t.Errorf("expected source %q but received %q", "overlay", m.Source)
}
var (
hash = hash("/tmp/layer2")
work = "workdir=" + filepath.Join(root, "active", hash, "work")
upper = "upperdir=" + filepath.Join(root, "active", hash, "fs")
lower = "lowerdir=" + filepath.Join(root, "snapshots", "base", "fs")
ah = hash("/tmp/layer2")
sh = hash("base")
work = "workdir=" + filepath.Join(root, "active", ah, "work")
upper = "upperdir=" + filepath.Join(root, "active", ah, "fs")
lower = "lowerdir=" + filepath.Join(root, "snapshots", sh, "fs")
)
for i, v := range []string{
work,
@ -133,7 +135,7 @@ func TestOverlayOverlayRead(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(root)
o, err := NewOverlay(root)
o, err := NewDriver(root)
if err != nil {
t.Error(err)
return
@ -177,3 +179,15 @@ func TestOverlayOverlayRead(t *testing.T) {
return
}
}
func TestOverlayDriverSuite(t *testing.T) {
testutil.RequiresRoot(t)
snapshot.DriverSuite(t, "Overlay", func(root string) (snapshot.Driver, func(), error) {
driver, err := NewDriver(root)
if err != nil {
t.Fatal(err)
}
return driver, func() {}, nil
})
}

View File

@ -49,7 +49,16 @@ func DumpDir(t *testing.T, root string) {
return err
}
t.Log(fi.Mode(), path)
if fi.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(path)
if err != nil {
return err
}
t.Log(fi.Mode(), path, "->", target)
} else {
t.Log(fi.Mode(), path)
}
return nil
}); err != nil {
t.Fatalf("error dumping directory: %v", err)