When a writer is requested for an object that already exists, add that object to the provided any lease to prevent other operations from affecting the current lease's use of that content. Signed-off-by: Derek McGowan <derek@mcgstyle.net>
113 lines
2.9 KiB
Go
113 lines
2.9 KiB
Go
package metadata
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/boltdb/bolt"
|
|
"github.com/containerd/containerd/content"
|
|
"github.com/containerd/containerd/content/local"
|
|
"github.com/containerd/containerd/content/testsuite"
|
|
"github.com/containerd/containerd/errdefs"
|
|
"github.com/containerd/containerd/leases"
|
|
"github.com/containerd/containerd/namespaces"
|
|
digest "github.com/opencontainers/go-digest"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func createContentStore(ctx context.Context, root string) (content.Store, func() error, error) {
|
|
// TODO: Use mocked or in-memory store
|
|
cs, err := local.NewStore(root)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
db, err := bolt.Open(filepath.Join(root, "metadata.db"), 0660, nil)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return NewDB(db, cs, nil).ContentStore(), func() error {
|
|
return db.Close()
|
|
}, nil
|
|
}
|
|
|
|
func TestContent(t *testing.T) {
|
|
testsuite.ContentSuite(t, "metadata", createContentStore)
|
|
}
|
|
|
|
func TestContentLeased(t *testing.T) {
|
|
ctx, db, cancel := testDB(t)
|
|
defer cancel()
|
|
|
|
cs := db.ContentStore()
|
|
|
|
blob := []byte("any content")
|
|
expected := digest.FromBytes(blob)
|
|
|
|
lctx, _, err := createLease(ctx, db, "lease-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := content.WriteBlob(lctx, cs, "test-1", bytes.NewReader(blob), int64(len(blob)), expected); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := checkContentLeased(lctx, db, expected); err != nil {
|
|
t.Fatal("lease checked failed:", err)
|
|
}
|
|
|
|
lctx, _, err = createLease(ctx, db, "lease-2")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := cs.Writer(lctx, "test-2", int64(len(blob)), expected); err == nil {
|
|
t.Fatal("expected already exist error")
|
|
} else if !errdefs.IsAlreadyExists(err) {
|
|
t.Fatal(err)
|
|
}
|
|
if err := checkContentLeased(lctx, db, expected); err != nil {
|
|
t.Fatal("lease checked failed:", err)
|
|
}
|
|
}
|
|
|
|
func createLease(ctx context.Context, db *DB, name string) (context.Context, func() error, error) {
|
|
if err := db.Update(func(tx *bolt.Tx) error {
|
|
_, err := NewLeaseManager(tx).Create(ctx, name, nil)
|
|
return err
|
|
}); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return leases.WithLease(ctx, name), func() error {
|
|
return db.Update(func(tx *bolt.Tx) error {
|
|
return NewLeaseManager(tx).Delete(ctx, name)
|
|
})
|
|
}, nil
|
|
}
|
|
|
|
func checkContentLeased(ctx context.Context, db *DB, dgst digest.Digest) error {
|
|
ns, ok := namespaces.Namespace(ctx)
|
|
if !ok {
|
|
return errors.New("no namespace in context")
|
|
}
|
|
lease, ok := leases.Lease(ctx)
|
|
if !ok {
|
|
return errors.New("no lease in context")
|
|
}
|
|
|
|
return db.View(func(tx *bolt.Tx) error {
|
|
bkt := getBucket(tx, bucketKeyVersion, []byte(ns), bucketKeyObjectLeases, []byte(lease), bucketKeyObjectContent)
|
|
if bkt == nil {
|
|
return errors.Wrapf(errdefs.ErrNotFound, "bucket not found", lease)
|
|
}
|
|
v := bkt.Get([]byte(dgst.String()))
|
|
if v == nil {
|
|
return errors.Wrap(errdefs.ErrNotFound, "object not leased")
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|