cachemanager: check pulled snapshot
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>docker-18.09
parent
ae05d02379
commit
fdde46f7ff
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue