2017-01-13 23:31:21 +00:00
|
|
|
package overlay
|
2016-12-09 19:27:30 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-02-03 02:55:53 +00:00
|
|
|
"io/ioutil"
|
2016-12-09 19:27:30 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2016-12-12 21:35:02 +00:00
|
|
|
"sync"
|
2016-12-09 19:27:30 +00:00
|
|
|
|
|
|
|
"github.com/docker/containerd"
|
2017-02-03 02:55:53 +00:00
|
|
|
digest "github.com/opencontainers/go-digest"
|
2016-12-09 19:27:30 +00:00
|
|
|
)
|
|
|
|
|
2017-02-03 02:55:53 +00:00
|
|
|
func NewDriver(root string) (*Overlay, error) {
|
2016-12-09 19:27:30 +00:00
|
|
|
if err := os.MkdirAll(root, 0700); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, p := range []string{
|
|
|
|
"snapshots",
|
|
|
|
"active",
|
|
|
|
} {
|
|
|
|
if err := os.MkdirAll(filepath.Join(root, p), 0700); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2017-01-17 08:42:27 +00:00
|
|
|
return &Overlay{
|
2016-12-12 21:35:02 +00:00
|
|
|
root: root,
|
|
|
|
cache: newCache(),
|
2016-12-09 19:27:30 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2017-01-17 08:42:27 +00:00
|
|
|
type Overlay struct {
|
2016-12-12 21:35:02 +00:00
|
|
|
root string
|
|
|
|
cache *cache
|
2016-12-09 19:27:30 +00:00
|
|
|
}
|
|
|
|
|
2017-02-03 02:55:53 +00:00
|
|
|
func (o *Overlay) Prepare(key, parent string) ([]containerd.Mount, error) {
|
2016-12-09 19:27:30 +00:00
|
|
|
active, err := o.newActiveDir(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-02-03 02:55:53 +00:00
|
|
|
if parent != "" {
|
|
|
|
if err := active.setParent(parent); err != nil {
|
2016-12-09 19:27:30 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2017-02-03 02:55:53 +00:00
|
|
|
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)
|
2016-12-12 21:35:02 +00:00
|
|
|
return active.mounts(o.cache)
|
2016-12-09 19:27:30 +00:00
|
|
|
}
|
|
|
|
|
2017-01-17 08:42:27 +00:00
|
|
|
func (o *Overlay) Commit(name, key string) error {
|
2016-12-09 19:27:30 +00:00
|
|
|
active := o.getActive(key)
|
2017-02-03 02:55:53 +00:00
|
|
|
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")
|
2016-12-09 19:27:30 +00:00
|
|
|
}
|
|
|
|
|
2017-01-17 08:42:27 +00:00
|
|
|
func (o *Overlay) newActiveDir(key string) (*activeDir, error) {
|
2016-12-09 19:27:30 +00:00
|
|
|
var (
|
2017-02-03 02:55:53 +00:00
|
|
|
path = filepath.Join(o.root, "active", hash(key))
|
2016-12-09 19:27:30 +00:00
|
|
|
)
|
|
|
|
a := &activeDir{
|
|
|
|
path: path,
|
|
|
|
snapshotsDir: filepath.Join(o.root, "snapshots"),
|
|
|
|
}
|
|
|
|
for _, p := range []string{
|
|
|
|
"work",
|
|
|
|
"fs",
|
|
|
|
} {
|
|
|
|
if err := os.MkdirAll(filepath.Join(path, p), 0700); err != nil {
|
|
|
|
a.delete()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
2017-01-17 08:42:27 +00:00
|
|
|
func (o *Overlay) getActive(key string) *activeDir {
|
2016-12-09 19:27:30 +00:00
|
|
|
return &activeDir{
|
|
|
|
path: filepath.Join(o.root, "active", hash(key)),
|
|
|
|
snapshotsDir: filepath.Join(o.root, "snapshots"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func hash(k string) string {
|
2017-02-03 02:55:53 +00:00
|
|
|
return digest.FromString(k).Hex()
|
2016-12-09 19:27:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type activeDir struct {
|
|
|
|
snapshotsDir string
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *activeDir) delete() error {
|
|
|
|
return os.RemoveAll(a.path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *activeDir) setParent(name string) error {
|
2017-02-03 02:55:53 +00:00
|
|
|
return os.Symlink(filepath.Join(a.snapshotsDir, hash(name)), filepath.Join(a.path, "parent"))
|
2016-12-09 19:27:30 +00:00
|
|
|
}
|
|
|
|
|
2017-02-03 02:55:53 +00:00
|
|
|
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.
|
2016-12-09 19:27:30 +00:00
|
|
|
if err := os.RemoveAll(filepath.Join(a.path, "work")); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-02-03 02:55:53 +00:00
|
|
|
|
|
|
|
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)))
|
2016-12-09 19:27:30 +00:00
|
|
|
}
|
|
|
|
|
2016-12-12 21:35:02 +00:00
|
|
|
func (a *activeDir) mounts(c *cache) ([]containerd.Mount, error) {
|
2016-12-09 19:27:30 +00:00
|
|
|
var (
|
2016-12-12 21:35:02 +00:00
|
|
|
parents []string
|
|
|
|
err error
|
|
|
|
current = a.path
|
2016-12-09 19:27:30 +00:00
|
|
|
)
|
|
|
|
for {
|
2016-12-12 21:35:02 +00:00
|
|
|
if current, err = c.get(current); err != nil {
|
2016-12-09 19:27:30 +00:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-12-12 21:35:02 +00:00
|
|
|
parents = append(parents, filepath.Join(current, "fs"))
|
2016-12-09 19:27:30 +00:00
|
|
|
}
|
|
|
|
if len(parents) == 0 {
|
|
|
|
// if we only have one layer/no parents then just return a bind mount as overlay
|
|
|
|
// will not work
|
|
|
|
return []containerd.Mount{
|
|
|
|
{
|
|
|
|
Source: filepath.Join(a.path, "fs"),
|
|
|
|
Type: "bind",
|
|
|
|
Options: []string{
|
|
|
|
"rw",
|
|
|
|
"rbind",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
options := []string{
|
|
|
|
fmt.Sprintf("workdir=%s", filepath.Join(a.path, "work")),
|
|
|
|
fmt.Sprintf("upperdir=%s", filepath.Join(a.path, "fs")),
|
|
|
|
fmt.Sprintf("lowerdir=%s", strings.Join(parents, ":")),
|
|
|
|
}
|
|
|
|
return []containerd.Mount{
|
|
|
|
{
|
|
|
|
Type: "overlay",
|
|
|
|
Source: "overlay",
|
|
|
|
Options: options,
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
2016-12-12 21:35:02 +00:00
|
|
|
|
|
|
|
func newCache() *cache {
|
|
|
|
return &cache{
|
|
|
|
parents: make(map[string]string),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type cache struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
parents map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cache) get(path string) (string, error) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
parentRoot, ok := c.parents[path]
|
|
|
|
if !ok {
|
|
|
|
link, err := os.Readlink(filepath.Join(path, "parent"))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
c.parents[path], parentRoot = link, link
|
|
|
|
}
|
|
|
|
return parentRoot, nil
|
|
|
|
}
|
2017-02-03 02:55:53 +00:00
|
|
|
|
|
|
|
func (c *cache) invalidate(path string) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
delete(c.parents, path)
|
|
|
|
}
|