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" "sync"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
cdsnapshot "github.com/containerd/containerd/snapshot"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/tonistiigi/buildkit_poc/snapshot" "github.com/tonistiigi/buildkit_poc/snapshot"
) )
@ -101,10 +102,34 @@ func (cm *cacheManager) Close() error {
func (cm *cacheManager) Get(id string) (ImmutableRef, error) { func (cm *cacheManager) Get(id string) (ImmutableRef, error) {
cm.mu.Lock() cm.mu.Lock()
defer cm.mu.Unlock() defer cm.mu.Unlock()
return cm.get(id)
}
func (cm *cacheManager) get(id string) (ImmutableRef, error) {
rec, ok := cm.records[id] rec, ok := cm.records[id]
if !ok { if !ok {
// TODO: lazy-load from Snapshotter info, err := cm.Snapshotter.Stat(context.TODO(), id)
return nil, errors.Wrapf(errNotFound, "%s not found", 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() rec.mu.Lock()

21
cache/refs.go vendored
View File

@ -39,6 +39,8 @@ type cacheRecord struct {
id string id string
cm *cacheManager cm *cacheManager
parent ImmutableRef parent ImmutableRef
view string
viewMount []mount.Mount
} }
// hold manager lock before calling // 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 nil, errors.Wrapf(err, "failed to mount %s", sr.id)
} }
return m, nil 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") return nil, errors.New("snapshot mount not implemented")
@ -83,6 +96,14 @@ func (sr *cacheRef) release() error {
return err 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) delete(sr.refs, sr)
sr.frozen = false sr.frozen = false

View File

@ -15,21 +15,23 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/rootfs" "github.com/containerd/containerd/rootfs"
"github.com/containerd/containerd/snapshot" cdsnapshot "github.com/containerd/containerd/snapshot"
"github.com/containerd/containerd/snapshot/naive" "github.com/containerd/containerd/snapshot/naive"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tonistiigi/buildkit_poc/cache" "github.com/tonistiigi/buildkit_poc/cache"
"github.com/tonistiigi/buildkit_poc/snapshot"
"github.com/tonistiigi/buildkit_poc/source" "github.com/tonistiigi/buildkit_poc/source"
"github.com/tonistiigi/buildkit_poc/source/containerimage" "github.com/tonistiigi/buildkit_poc/source/containerimage"
) )
func TestControl(t *testing.T) { func TestControl(t *testing.T) {
// this should be an example or e2e test
tmpdir, err := ioutil.TempDir("", "controltest") tmpdir, err := ioutil.TempDir("", "controltest")
assert.NoError(t, err) assert.NoError(t, err)
// defer os.RemoveAll(tmpdir) defer os.RemoveAll(tmpdir)
cd, err := localContainerd(tmpdir) cd, err := localContainerd(tmpdir)
assert.NoError(t, err) assert.NoError(t, err)
@ -46,7 +48,7 @@ func TestControl(t *testing.T) {
Snapshotter: cd.Snapshotter, Snapshotter: cd.Snapshotter,
ContentStore: cd.ContentStore, ContentStore: cd.ContentStore,
Applier: cd.Applier, Applier: cd.Applier,
Accessor: cm, CacheAccessor: cm,
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -58,11 +60,33 @@ func TestControl(t *testing.T) {
snap, err := sm.Pull(context.TODO(), img) snap, err := sm.Pull(context.TODO(), img)
assert.NoError(t, err) 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 { type containerd struct {
Snapshotter snapshot.Snapshotter Snapshotter cdsnapshot.Snapshotter
ContentStore content.Store ContentStore content.Store
Applier rootfs.Applier Applier rootfs.Applier
} }
@ -149,15 +173,3 @@ func (rc *readCounter) Read(p []byte) (n int, err error) {
rc.c += int64(n) rc.c += int64(n)
return 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" "context"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/snapshot"
) )
// Snapshotter defines interface that any snapshot implementation should satisfy // Snapshotter defines interface that any snapshot implementation should satisfy
type Snapshotter interface { type Snapshotter interface {
Stat(ctx context.Context, key string) (snapshot.Info, error)
Prepare(ctx context.Context, key, parent string) ([]mount.Mount, error) Prepare(ctx context.Context, key, parent string) ([]mount.Mount, error)
Mounts(ctx context.Context, key string) ([]mount.Mount, error) Mounts(ctx context.Context, key string) ([]mount.Mount, error)
Commit(ctx context.Context, name, key string) 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 Snapshotter snapshot.Snapshotter
ContentStore content.Store ContentStore content.Store
Applier rootfs.Applier Applier rootfs.Applier
Accessor cache.Accessor CacheAccessor cache.Accessor
} }
type imageSource struct { type imageSource struct {
@ -64,6 +64,9 @@ func (is *imageSource) Pull(ctx context.Context, id source.Identifier) (cache.Im
return nil, err 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{ handlers := []images.Handler{
remotes.FetchHandler(is.ContentStore, fetcher), remotes.FetchHandler(is.ContentStore, fetcher),
images.ChildrenHandler(is.ContentStore), images.ChildrenHandler(is.ContentStore),
@ -72,24 +75,25 @@ func (is *imageSource) Pull(ctx context.Context, id source.Identifier) (cache.Im
return nil, err 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, 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) layers, err := getLayers(ctx, is.ContentStore, desc)
if err != nil { 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 { if err != nil {
return err return "", err
} }
_ = chainid return string(chainID), nil
return nil
} }
func getLayers(ctx context.Context, provider content.Provider, desc ocispec.Descriptor) ([]rootfs.Layer, error) { func getLayers(ctx context.Context, provider content.Provider, desc ocispec.Descriptor) ([]rootfs.Layer, error) {