Skip to content

Commit

Permalink
Merge pull request #10199 from dmcgowan/1.6-metadata-add-lease-exists
Browse files Browse the repository at this point in the history
[release/1.6] Update metadata snapshotter to lease on already exists
  • Loading branch information
mxpv committed May 11, 2024
2 parents 6d429d6 + 57860c1 commit b3d7fec
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 26 deletions.
34 changes: 25 additions & 9 deletions metadata/snapshot.go
Expand Up @@ -307,6 +307,7 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re
bopts = []snapshots.Opt{
snapshots.WithLabels(snapshots.FilterInheritedLabels(base.Labels)),
}
rerr error
)

if err := update(ctx, s.db, func(tx *bolt.Tx) error {
Expand All @@ -318,12 +319,20 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re
// Check if target exists, if so, return already exists
if target != "" {
if tbkt := bkt.Bucket([]byte(target)); tbkt != nil {
return fmt.Errorf("target snapshot %q: %w", target, errdefs.ErrAlreadyExists)
rerr = fmt.Errorf("target snapshot %q: %w", target, errdefs.ErrAlreadyExists)
if err := addSnapshotLease(ctx, tx, s.name, target); err != nil {
return err
}
return nil
}
}

if bbkt := bkt.Bucket([]byte(key)); bbkt != nil {
return fmt.Errorf("snapshot %q: %w", key, errdefs.ErrAlreadyExists)
rerr = fmt.Errorf("snapshot %q: %w", key, errdefs.ErrAlreadyExists)
if err := addSnapshotLease(ctx, tx, s.name, key); err != nil {
return err
}
return nil
}

if parent != "" {
Expand All @@ -344,11 +353,14 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re
}); err != nil {
return nil, err
}
// Already exists and lease successfully added in transaction
if rerr != nil {
return nil, rerr
}

var (
m []mount.Mount
created string
rerr error
)
if readonly {
m, err = s.Snapshotter.View(ctx, bkey, bparent, bopts...)
Expand Down Expand Up @@ -511,24 +523,28 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
return err
}

var bname string
var (
bname string
rerr error
)
if err := update(ctx, s.db, func(tx *bolt.Tx) error {
bkt := getSnapshotterBucket(tx, ns, s.name)
if bkt == nil {
return fmt.Errorf("can not find snapshotter %q: %w",
s.name, errdefs.ErrNotFound)
}

if err := addSnapshotLease(ctx, tx, s.name, name); err != nil {
return err
}
bbkt, err := bkt.CreateBucket([]byte(name))
if err != nil {
if err == bolt.ErrBucketExists {
err = fmt.Errorf("snapshot %q: %w", name, errdefs.ErrAlreadyExists)
rerr = fmt.Errorf("snapshot %q: %w", name, errdefs.ErrAlreadyExists)
return nil
}
return err
}
if err := addSnapshotLease(ctx, tx, s.name, name); err != nil {
return err
}

obkt := bkt.Bucket([]byte(key))
if obkt == nil {
Expand Down Expand Up @@ -618,7 +634,7 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
return err
}

return nil
return rerr

}

Expand Down
103 changes: 86 additions & 17 deletions metadata/snapshot_test.go
Expand Up @@ -29,6 +29,7 @@ import (

"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/testutil"
Expand Down Expand Up @@ -63,6 +64,32 @@ func newTestSnapshotter(ctx context.Context, root string) (snapshots.Snapshotter
}, nil
}

func snapshotLease(ctx context.Context, t *testing.T, db *DB, sn string) (context.Context, func(string) bool) {
lm := NewLeaseManager(db)
l, err := lm.Create(ctx, leases.WithRandomID())
if err != nil {
t.Fatal(err)
}
ltype := fmt.Sprintf("%s/%s", bucketKeyObjectSnapshots, sn)

t.Cleanup(func() {
lm.Delete(ctx, l)

})
return leases.WithLease(ctx, l.ID), func(id string) bool {
resources, err := lm.ListResources(ctx, l)
if err != nil {
t.Error(err)
}
for _, r := range resources {
if r.Type == ltype && r.ID == id {
return true
}
}
return false
}
}

func TestMetadata(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("snapshotter not implemented on windows")
Expand All @@ -78,121 +105,163 @@ func TestSnapshotterWithRef(t *testing.T) {
}))
defer done()

sn := db.Snapshotter("tmp")
snapshotter := "tmp"
ctx1, leased1 := snapshotLease(ctx, t, db, snapshotter)
sn := db.Snapshotter(snapshotter)

key1 := "test1"
test1opt := snapshots.WithLabels(
map[string]string{
labelSnapshotRef: "test1",
labelSnapshotRef: key1,
},
)

_, err := sn.Prepare(ctx, "test1-tmp", "", test1opt)
key1t := "test1-tmp"
_, err := sn.Prepare(ctx1, key1t, "", test1opt)
if err != nil {
t.Fatal(err)
}
if !leased1(key1t) {
t.Errorf("no lease for %q", key1t)
}

err = sn.Commit(ctx, "test1", "test1-tmp", test1opt)
err = sn.Commit(ctx1, key1, key1t, test1opt)
if err != nil {
t.Fatal(err)
}
if !leased1(key1) {
t.Errorf("no lease for %q", key1)
}
if leased1(key1t) {
t.Errorf("lease should be removed for %q", key1t)
}

ctx2 := namespaces.WithNamespace(ctx, "testing2")

_, err = sn.Prepare(ctx2, "test1-tmp", "", test1opt)
_, err = sn.Prepare(ctx2, key1t, "", test1opt)
if err == nil {
t.Fatal("expected already exists error")
} else if !errdefs.IsAlreadyExists(err) {
t.Fatal(err)
}

// test1 should now be in the namespace
_, err = sn.Stat(ctx2, "test1")
_, err = sn.Stat(ctx2, key1)
if err != nil {
t.Fatal(err)
}

key2t := "test2-tmp"
key2 := "test2"
test2opt := snapshots.WithLabels(
map[string]string{
labelSnapshotRef: "test2",
labelSnapshotRef: key2,
},
)

_, err = sn.Prepare(ctx2, "test2-tmp", "test1", test2opt)
_, err = sn.Prepare(ctx2, key2t, key1, test2opt)
if err != nil {
t.Fatal(err)
}

// In original namespace, but not committed
_, err = sn.Prepare(ctx, "test2-tmp", "test1", test2opt)
_, err = sn.Prepare(ctx1, key2t, key1, test2opt)
if err != nil {
t.Fatal(err)
}
if !leased1(key2t) {
t.Errorf("no lease for %q", key2t)
}
if leased1(key2) {
t.Errorf("lease for %q should not exist yet", key2)
}

err = sn.Commit(ctx2, "test2", "test2-tmp", test2opt)
err = sn.Commit(ctx2, key2, key2t, test2opt)
if err != nil {
t.Fatal(err)
}

// See note in Commit function for why
// this does not return ErrAlreadyExists
err = sn.Commit(ctx, "test2", "test2-tmp", test2opt)
err = sn.Commit(ctx1, key2, key2t, test2opt)
if err != nil {
t.Fatal(err)
}

ctx2, leased2 := snapshotLease(ctx2, t, db, snapshotter)
if leased2(key2) {
t.Errorf("new lease should not have previously created snapshots")
}
// This should error out, already exists in namespace
// despite mismatched parent
_, err = sn.Prepare(ctx2, "test2-tmp-again", "", test2opt)
key2ta := "test2-tmp-again"
_, err = sn.Prepare(ctx2, key2ta, "", test2opt)
if err == nil {
t.Fatal("expected already exists error")
} else if !errdefs.IsAlreadyExists(err) {
t.Fatal(err)
}
if !leased2(key2) {
t.Errorf("no lease for %q", key2)
}

// In original namespace, but already exists
_, err = sn.Prepare(ctx, "test2-tmp-again", "test1", test2opt)
_, err = sn.Prepare(ctx, key2ta, key1, test2opt)
if err == nil {
t.Fatal("expected already exists error")
} else if !errdefs.IsAlreadyExists(err) {
t.Fatal(err)
}
if leased1(key2ta) {
t.Errorf("should not have lease for non-existent snapshot %q", key2ta)
}

// Now try a third namespace

ctx3 := namespaces.WithNamespace(ctx, "testing3")
ctx3, leased3 := snapshotLease(ctx3, t, db, snapshotter)

// This should error out, matching parent not found
_, err = sn.Prepare(ctx3, "test2-tmp", "", test2opt)
_, err = sn.Prepare(ctx3, key2t, "", test2opt)
if err != nil {
t.Fatal(err)
}

// Remove, not going to use yet
err = sn.Remove(ctx3, "test2-tmp")
err = sn.Remove(ctx3, key2t)
if err != nil {
t.Fatal(err)
}

_, err = sn.Prepare(ctx3, "test2-tmp", "test1", test2opt)
_, err = sn.Prepare(ctx3, key2t, key1, test2opt)
if err == nil {
t.Fatal("expected not error")
} else if !errdefs.IsNotFound(err) {
t.Fatal(err)
}
if leased3(key1) {
t.Errorf("lease for %q should not have been created", key1)
}

_, err = sn.Prepare(ctx3, "test1-tmp", "", test1opt)
_, err = sn.Prepare(ctx3, key1t, "", test1opt)
if err == nil {
t.Fatal("expected already exists error")
} else if !errdefs.IsAlreadyExists(err) {
t.Fatal(err)
}
if !leased3(key1) {
t.Errorf("no lease for %q", key1)
}

_, err = sn.Prepare(ctx3, "test2-tmp", "test1", test2opt)
if err == nil {
t.Fatal("expected already exists error")
} else if !errdefs.IsAlreadyExists(err) {
t.Fatal(err)
}
if !leased3(key2) {
t.Errorf("no lease for %q", key2)
}
}

func TestFilterInheritedLabels(t *testing.T) {
Expand Down

0 comments on commit b3d7fec

Please sign in to comment.