cachemanager: check pulled snapshot

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
docker-18.09
Tonis Tiigi 2017-05-30 14:17:04 -07:00
parent ae05d02379
commit fdde46f7ff
5 changed files with 104 additions and 38 deletions

29
cache/manager.go vendored
View File

@ -7,6 +7,7 @@ import (
"sync"
"github.com/boltdb/bolt"
cdsnapshot "github.com/containerd/containerd/snapshot"
"github.com/pkg/errors"
"github.com/tonistiigi/buildkit_poc/snapshot"
)
@ -101,10 +102,34 @@ func (cm *cacheManager) Close() error {
func (cm *cacheManager) Get(id string) (ImmutableRef, error) {
cm.mu.Lock()
defer cm.mu.Unlock()
return cm.get(id)
}
func (cm *cacheManager) get(id string) (ImmutableRef, error) {
rec, ok := cm.records[id]
if !ok {
// TODO: lazy-load from Snapshotter
return nil, errors.Wrapf(errNotFound, "%s not found", id)
info, err := cm.Snapshotter.Stat(context.TODO(), id)
if err != nil {
return nil, err
}
if info.Kind != cdsnapshot.KindCommitted {
return nil, errors.Wrapf(errInvalid, "can't lazy load active %s", id)
}
var parent ImmutableRef
if info.Parent != "" {
parent, err = cm.get(info.Parent)
if err != nil {
return nil, err
}
}
rec = &cacheRecord{
id: id,
cm: cm,
refs: make(map[*cacheRef]struct{}),
parent: parent,
}
cm.records[id] = rec // TODO: store to db
}
rec.mu.Lock()

21
cache/refs.go vendored
View File

@ -39,6 +39,8 @@ type cacheRecord struct {
id string
cm *cacheManager
parent ImmutableRef
view string
viewMount []mount.Mount
}
// hold manager lock before calling
@ -62,6 +64,17 @@ func (sr *cacheRef) Mount() ([]mount.Mount, error) {
return nil, errors.Wrapf(err, "failed to mount %s", sr.id)
}
return m, nil
} else {
if sr.viewMount == nil { // TODO: handle this better
sr.view = generateID()
m, err := sr.cm.Snapshotter.View(context.TODO(), sr.view, sr.id)
if err != nil {
sr.view = ""
return nil, errors.Wrapf(err, "failed to mount %s", sr.id)
}
sr.viewMount = m
}
return sr.viewMount, nil
}
return nil, errors.New("snapshot mount not implemented")
@ -83,6 +96,14 @@ func (sr *cacheRef) release() error {
return err
}
}
if sr.viewMount != nil {
if err := sr.cm.Snapshotter.Remove(context.TODO(), sr.view); err != nil {
return err
}
sr.view = ""
sr.viewMount = nil
}
delete(sr.refs, sr)
sr.frozen = false

View File

@ -15,21 +15,23 @@ import (
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/rootfs"
"github.com/containerd/containerd/snapshot"
cdsnapshot "github.com/containerd/containerd/snapshot"
"github.com/containerd/containerd/snapshot/naive"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/tonistiigi/buildkit_poc/cache"
"github.com/tonistiigi/buildkit_poc/snapshot"
"github.com/tonistiigi/buildkit_poc/source"
"github.com/tonistiigi/buildkit_poc/source/containerimage"
)
func TestControl(t *testing.T) {
// this should be an example or e2e test
tmpdir, err := ioutil.TempDir("", "controltest")
assert.NoError(t, err)
// defer os.RemoveAll(tmpdir)
defer os.RemoveAll(tmpdir)
cd, err := localContainerd(tmpdir)
assert.NoError(t, err)
@ -46,7 +48,7 @@ func TestControl(t *testing.T) {
Snapshotter: cd.Snapshotter,
ContentStore: cd.ContentStore,
Applier: cd.Applier,
Accessor: cm,
CacheAccessor: cm,
})
assert.NoError(t, err)
@ -58,11 +60,33 @@ func TestControl(t *testing.T) {
snap, err := sm.Pull(context.TODO(), img)
assert.NoError(t, err)
_ = snap
mounts, err := snap.Mount()
assert.NoError(t, err)
lm := snapshot.LocalMounter(mounts)
target, err := lm.Mount()
assert.NoError(t, err)
f, err := os.Open(target)
assert.NoError(t, err)
names, err := f.Readdirnames(-1)
assert.NoError(t, err)
assert.True(t, len(names) > 10)
err = f.Close()
assert.NoError(t, err)
lm.Unmount()
assert.NoError(t, err)
err = snap.Release()
assert.NoError(t, err)
}
type containerd struct {
Snapshotter snapshot.Snapshotter
Snapshotter cdsnapshot.Snapshotter
ContentStore content.Store
Applier rootfs.Applier
}
@ -149,15 +173,3 @@ func (rc *readCounter) Read(p []byte) (n int, err error) {
rc.c += int64(n)
return
}
// req := &diffapi.ApplyRequest{
// Diff: fromDescriptor(diff),
// Mounts: fromMounts(mounts),
// }
// resp, err := r.client.Apply(ctx, req)
// if err != nil {
// return ocispec.Descriptor{}, err
// }
// return toDescriptor(resp.Applied), nil
// Apply(context.Context, ocispec.Descriptor, []mount.Mount) (ocispec.Descriptor, error)

View File

@ -4,11 +4,15 @@ import (
"context"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/snapshot"
)
// Snapshotter defines interface that any snapshot implementation should satisfy
type Snapshotter interface {
Stat(ctx context.Context, key string) (snapshot.Info, error)
Prepare(ctx context.Context, key, parent string) ([]mount.Mount, error)
Mounts(ctx context.Context, key string) ([]mount.Mount, error)
Commit(ctx context.Context, name, key string) error
View(ctx context.Context, key, parent string) ([]mount.Mount, error)
Remove(ctx context.Context, key string) error
}

View File

@ -24,7 +24,7 @@ type SourceOpt struct {
Snapshotter snapshot.Snapshotter
ContentStore content.Store
Applier rootfs.Applier
Accessor cache.Accessor
CacheAccessor cache.Accessor
}
type imageSource struct {
@ -64,6 +64,9 @@ func (is *imageSource) Pull(ctx context.Context, id source.Identifier) (cache.Im
return nil, err
}
// TODO: need a wrapper snapshot interface that combines content
// and snapshots as 1) buildkit shouldn't have a dependency on contentstore
// or 2) cachemanager should manage the contentstore
handlers := []images.Handler{
remotes.FetchHandler(is.ContentStore, fetcher),
images.ChildrenHandler(is.ContentStore),
@ -72,24 +75,25 @@ func (is *imageSource) Pull(ctx context.Context, id source.Identifier) (cache.Im
return nil, err
}
if err := is.unpack(ctx, desc); err != nil {
chainid, err := is.unpack(ctx, desc)
if err != nil {
return nil, err
}
return nil, nil
return is.CacheAccessor.Get(chainid)
}
func (is *imageSource) unpack(ctx context.Context, desc ocispec.Descriptor) error {
func (is *imageSource) unpack(ctx context.Context, desc ocispec.Descriptor) (string, error) {
layers, err := getLayers(ctx, is.ContentStore, desc)
if err != nil {
return err
return "", err
}
chainid, err := rootfs.ApplyLayers(ctx, layers, is.Snapshotter, is.Applier)
chainID, err := rootfs.ApplyLayers(ctx, layers, is.Snapshotter, is.Applier)
if err != nil {
return err
return "", err
}
_ = chainid
return nil
return string(chainID), nil
}
func getLayers(ctx context.Context, provider content.Provider, desc ocispec.Descriptor) ([]rootfs.Layer, error) {