cachemanager: new active creation

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
docker-18.09
Tonis Tiigi 2017-05-26 10:20:41 -07:00
parent 5509c18410
commit f2739f0728
5 changed files with 152 additions and 13 deletions

View File

@ -2,12 +2,15 @@ package cachemanager
import (
"context"
"crypto/rand"
"encoding/hex"
"os"
"path/filepath"
"sync"
"time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/mount"
"github.com/pkg/errors"
"github.com/tonistiigi/buildkit_poc/snapshot"
)
@ -45,23 +48,18 @@ type SnapshotRef interface {
type ActiveRef interface {
Mountable
Release() (SnapshotRef, error)
ReleaseActive() (SnapshotRef, error)
ReleaseAndCommit(ctx context.Context) (SnapshotRef, error)
Size() (int64, error)
}
type Mountable interface {
Mount() (Mount, error)
}
type Mount interface { // replace with containerd.Mount
Mount() (string, error)
Unmount() error
Mount() ([]mount.Mount, error)
}
type CacheAccessor interface {
Get(id string) (SnapshotRef, error)
New(id string, s SnapshotRef) (ActiveRef, error)
New(s SnapshotRef) (ActiveRef, error)
GetActive(id string) (ActiveRef, error) // Rebase?
}
@ -133,8 +131,25 @@ func (cm *cacheManager) Get(id string) (SnapshotRef, error) {
}
return rec.ref(), nil
}
func (cm *cacheManager) New(id string, s SnapshotRef) (ActiveRef, error) {
return nil, errors.New("New not implemented")
func (cm *cacheManager) New(s SnapshotRef) (ActiveRef, error) {
if s != nil {
return nil, errors.New("snapshot parents not implemented") // TODO
}
id := generateID()
if _, err := cm.Snapshotter.Prepare(context.TODO(), id, ""); err != nil {
return nil, errors.Wrapf(err, "failed to prepare %s", id)
}
rec := &cacheRecord{
active: true,
id: id,
cm: cm,
refs: make(map[*snapshotRef]struct{}),
}
return rec.ref(), nil
}
func (cm *cacheManager) GetActive(id string) (ActiveRef, error) { // Rebase?
return nil, errors.New("GetActive not implemented")
@ -153,9 +168,12 @@ func (cm *cacheManager) GC(ctx context.Context) error {
}
type cacheRecord struct {
mu sync.Mutex
active bool
// meta SnapMeta
refs map[*snapshotRef]struct{}
id string
cm *cacheManager
}
// hold manager lock before calling
@ -169,14 +187,41 @@ type snapshotRef struct {
*cacheRecord
}
func (sr *snapshotRef) Mount() (Mount, error) {
return nil, errors.New("Mount not implemented")
func (sr *snapshotRef) Mount() ([]mount.Mount, error) {
sr.mu.Lock()
defer sr.mu.Unlock()
if sr.active {
m, err := sr.cm.Snapshotter.Mounts(context.TODO(), sr.id)
if err != nil {
return nil, errors.Wrapf(err, "failed to mount %s", sr.id)
}
return m, nil
}
return nil, errors.New("snapshot mount not implemented")
}
func (sr *snapshotRef) Release() error {
return errors.New("Release not implemented")
}
func (sr *snapshotRef) ReleaseActive() (SnapshotRef, error) {
return nil, errors.New("ReleaseActive not implemented")
}
func (sr *snapshotRef) ReleaseAndCommit(ctx context.Context) (SnapshotRef, error) {
return nil, errors.New("ReleaseActive not implemented")
}
func (sr *snapshotRef) Size() (int64, error) {
return -1, errors.New("Size not implemented")
}
func generateID() string {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return hex.EncodeToString(b)
}

View File

@ -3,9 +3,12 @@ package cachemanager
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/containerd/containerd/snapshot/naive"
"github.com/stretchr/testify/assert"
"github.com/tonistiigi/buildkit_poc/snapshot"
)
func TestCacheManager(t *testing.T) {
@ -13,12 +16,35 @@ func TestCacheManager(t *testing.T) {
assert.NoError(t, err)
defer os.RemoveAll(tmpdir)
cm, err := NewCacheManager(CacheManagerOpt{Root: tmpdir})
snapshotter, err := naive.NewSnapshotter(filepath.Join(tmpdir, "snapshots"))
assert.NoError(t, err)
cm, err := NewCacheManager(CacheManagerOpt{
Root: tmpdir,
Snapshotter: snapshotter,
})
assert.NoError(t, err)
_, err = cm.Get("foobar")
assert.Error(t, err)
active, err := cm.New(nil)
assert.NoError(t, err)
m, err := active.Mount()
assert.NoError(t, err)
lm := snapshot.LocalMounter(m)
target, err := lm.Mount()
assert.NoError(t, err)
fi, err := os.Stat(target)
assert.NoError(t, err)
assert.Equal(t, fi.IsDir(), true)
err = lm.Unmount()
assert.NoError(t, err)
err = cm.Close()
assert.NoError(t, err)
}

View File

@ -1,4 +1,5 @@
FROM golang:1.8-alpine AS vndr
RUN apk add --no-cache g++
WORKDIR /go/src/github.com/tonistiigi/buildkit_poc
COPY . .
RUN go test ./...

59
snapshot/localmounter.go Normal file
View File

@ -0,0 +1,59 @@
package snapshot
import (
"io/ioutil"
"os"
"sync"
"github.com/containerd/containerd/mount"
"github.com/pkg/errors"
)
type Mounter interface {
Mount() (string, error)
Unmount() error
}
func LocalMounter(m []mount.Mount) Mounter {
return &localMounter{m: m}
}
type localMounter struct {
mu sync.Mutex
m []mount.Mount
target string
}
func (lm *localMounter) Mount() (string, error) {
lm.mu.Lock()
defer lm.mu.Unlock()
if len(lm.m) == 1 && lm.m[0].Type == "bind" {
return lm.m[0].Source, nil
}
dir, err := ioutil.TempDir("", "buildkit-mount")
if err != nil {
return "", errors.Wrap(err, "failed to create temp dir")
}
if err := mount.MountAll(lm.m, dir); err != nil {
os.RemoveAll(dir)
return "", err
}
lm.target = dir
return "", nil
}
func (lm *localMounter) Unmount() error {
lm.mu.Lock()
defer lm.mu.Unlock()
if lm.target != "" {
if err := mount.Unmount(lm.target, 0); err != nil {
return err
}
lm.target = ""
}
return nil
}

View File

@ -1,5 +1,13 @@
package snapshot
import (
"context"
"github.com/containerd/containerd/mount"
)
// Snapshotter defines interface that any snapshot implementation should satisfy
type Snapshotter interface {
Prepare(ctx context.Context, key, parent string) ([]mount.Mount, error)
Mounts(ctx context.Context, key string) ([]mount.Mount, error)
}