Skip to content

Commit f690928

Browse files
author
Maksym Pavlenko
authored
Merge pull request containerd#10187 from dmcgowan/metadata-add-lease-on-prepare
Update metadata snapshotter to lease on already exists
2 parents d2f1607 + 8c6183d commit f690928

File tree

2 files changed

+120
-33
lines changed

2 files changed

+120
-33
lines changed

core/metadata/snapshot.go

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re
323323
bopts = []snapshots.Opt{
324324
snapshots.WithLabels(snapshots.FilterInheritedLabels(base.Labels)),
325325
}
326+
rerr error
326327
)
327328

328329
if err := update(ctx, s.db, func(tx *bolt.Tx) error {
@@ -334,12 +335,20 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re
334335
// Check if target exists, if so, return already exists
335336
if target != "" {
336337
if tbkt := bkt.Bucket([]byte(target)); tbkt != nil {
337-
return fmt.Errorf("target snapshot %q: %w", target, errdefs.ErrAlreadyExists)
338+
rerr = fmt.Errorf("target snapshot %q: %w", target, errdefs.ErrAlreadyExists)
339+
if err := addSnapshotLease(ctx, tx, s.name, target); err != nil {
340+
return err
341+
}
342+
return nil
338343
}
339344
}
340345

341346
if bbkt := bkt.Bucket([]byte(key)); bbkt != nil {
342-
return fmt.Errorf("snapshot %q: %w", key, errdefs.ErrAlreadyExists)
347+
rerr = fmt.Errorf("snapshot %q: %w", key, errdefs.ErrAlreadyExists)
348+
if err := addSnapshotLease(ctx, tx, s.name, key); err != nil {
349+
return err
350+
}
351+
return nil
343352
}
344353

345354
if parent != "" {
@@ -360,11 +369,14 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re
360369
}); err != nil {
361370
return nil, err
362371
}
372+
// Already exists and lease successfully added in transaction
373+
if rerr != nil {
374+
return nil, rerr
375+
}
363376

364377
var (
365378
m []mount.Mount
366379
created string
367-
rerr error
368380
)
369381
if readonly {
370382
m, err = s.Snapshotter.View(ctx, bkey, bparent, bopts...)
@@ -527,24 +539,28 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
527539
return err
528540
}
529541

530-
var bname string
542+
var (
543+
bname string
544+
rerr error
545+
)
531546
if err := update(ctx, s.db, func(tx *bolt.Tx) error {
532547
bkt := getSnapshotterBucket(tx, ns, s.name)
533548
if bkt == nil {
534549
return fmt.Errorf("can not find snapshotter %q: %w",
535550
s.name, errdefs.ErrNotFound)
536551
}
537552

553+
if err := addSnapshotLease(ctx, tx, s.name, name); err != nil {
554+
return err
555+
}
538556
bbkt, err := bkt.CreateBucket([]byte(name))
539557
if err != nil {
540558
if err == bolt.ErrBucketExists {
541-
err = fmt.Errorf("snapshot %q: %w", name, errdefs.ErrAlreadyExists)
559+
rerr = fmt.Errorf("snapshot %q: %w", name, errdefs.ErrAlreadyExists)
560+
return nil
542561
}
543562
return err
544563
}
545-
if err := addSnapshotLease(ctx, tx, s.name, name); err != nil {
546-
return err
547-
}
548564

549565
obkt := bkt.Bucket([]byte(key))
550566
if obkt == nil {
@@ -634,17 +650,19 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
634650
return err
635651
}
636652

637-
if publisher := s.db.Publisher(ctx); publisher != nil {
638-
if err := publisher.Publish(ctx, "/snapshot/commit", &eventstypes.SnapshotCommit{
639-
Key: key,
640-
Name: name,
641-
Snapshotter: s.name,
642-
}); err != nil {
643-
return err
653+
if rerr == nil {
654+
if publisher := s.db.Publisher(ctx); publisher != nil {
655+
if err := publisher.Publish(ctx, "/snapshot/commit", &eventstypes.SnapshotCommit{
656+
Key: key,
657+
Name: name,
658+
Snapshotter: s.name,
659+
}); err != nil {
660+
return err
661+
}
644662
}
645663
}
646664

647-
return nil
665+
return rerr
648666

649667
}
650668

core/metadata/snapshot_test.go

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"testing"
2828
"time"
2929

30+
"github.com/containerd/containerd/v2/core/leases"
3031
"github.com/containerd/containerd/v2/core/mount"
3132
"github.com/containerd/containerd/v2/core/snapshots"
3233
"github.com/containerd/containerd/v2/core/snapshots/testsuite"
@@ -63,6 +64,32 @@ func newTestSnapshotter(ctx context.Context, root string) (snapshots.Snapshotter
6364
}, nil
6465
}
6566

67+
func snapshotLease(ctx context.Context, t *testing.T, db *DB, sn string) (context.Context, func(string) bool) {
68+
lm := NewLeaseManager(db)
69+
l, err := lm.Create(ctx, leases.WithRandomID())
70+
if err != nil {
71+
t.Fatal(err)
72+
}
73+
ltype := fmt.Sprintf("%s/%s", bucketKeyObjectSnapshots, sn)
74+
75+
t.Cleanup(func() {
76+
lm.Delete(ctx, l)
77+
78+
})
79+
return leases.WithLease(ctx, l.ID), func(id string) bool {
80+
resources, err := lm.ListResources(ctx, l)
81+
if err != nil {
82+
t.Error(err)
83+
}
84+
for _, r := range resources {
85+
if r.Type == ltype && r.ID == id {
86+
return true
87+
}
88+
}
89+
return false
90+
}
91+
}
92+
6693
func TestMetadata(t *testing.T) {
6794
if runtime.GOOS == "windows" {
6895
t.Skip("snapshotter not implemented on windows")
@@ -76,121 +103,163 @@ func TestSnapshotterWithRef(t *testing.T) {
76103
ctx, db := testDB(t, withSnapshotter("tmp", func(string) (snapshots.Snapshotter, error) {
77104
return NewTmpSnapshotter(), nil
78105
}))
79-
sn := db.Snapshotter("tmp")
106+
snapshotter := "tmp"
107+
ctx1, leased1 := snapshotLease(ctx, t, db, snapshotter)
108+
sn := db.Snapshotter(snapshotter)
80109

110+
key1 := "test1"
81111
test1opt := snapshots.WithLabels(
82112
map[string]string{
83-
labelSnapshotRef: "test1",
113+
labelSnapshotRef: key1,
84114
},
85115
)
86116

87-
_, err := sn.Prepare(ctx, "test1-tmp", "", test1opt)
117+
key1t := "test1-tmp"
118+
_, err := sn.Prepare(ctx1, key1t, "", test1opt)
88119
if err != nil {
89120
t.Fatal(err)
90121
}
122+
if !leased1(key1t) {
123+
t.Errorf("no lease for %q", key1t)
124+
}
91125

92-
err = sn.Commit(ctx, "test1", "test1-tmp", test1opt)
126+
err = sn.Commit(ctx1, key1, key1t, test1opt)
93127
if err != nil {
94128
t.Fatal(err)
95129
}
130+
if !leased1(key1) {
131+
t.Errorf("no lease for %q", key1)
132+
}
133+
if leased1(key1t) {
134+
t.Errorf("lease should be removed for %q", key1t)
135+
}
96136

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

99-
_, err = sn.Prepare(ctx2, "test1-tmp", "", test1opt)
139+
_, err = sn.Prepare(ctx2, key1t, "", test1opt)
100140
if err == nil {
101141
t.Fatal("expected already exists error")
102142
} else if !errdefs.IsAlreadyExists(err) {
103143
t.Fatal(err)
104144
}
105145

106146
// test1 should now be in the namespace
107-
_, err = sn.Stat(ctx2, "test1")
147+
_, err = sn.Stat(ctx2, key1)
108148
if err != nil {
109149
t.Fatal(err)
110150
}
111151

152+
key2t := "test2-tmp"
153+
key2 := "test2"
112154
test2opt := snapshots.WithLabels(
113155
map[string]string{
114-
labelSnapshotRef: "test2",
156+
labelSnapshotRef: key2,
115157
},
116158
)
117159

118-
_, err = sn.Prepare(ctx2, "test2-tmp", "test1", test2opt)
160+
_, err = sn.Prepare(ctx2, key2t, key1, test2opt)
119161
if err != nil {
120162
t.Fatal(err)
121163
}
122164

123165
// In original namespace, but not committed
124-
_, err = sn.Prepare(ctx, "test2-tmp", "test1", test2opt)
166+
_, err = sn.Prepare(ctx1, key2t, key1, test2opt)
125167
if err != nil {
126168
t.Fatal(err)
127169
}
170+
if !leased1(key2t) {
171+
t.Errorf("no lease for %q", key2t)
172+
}
173+
if leased1(key2) {
174+
t.Errorf("lease for %q should not exist yet", key2)
175+
}
128176

129-
err = sn.Commit(ctx2, "test2", "test2-tmp", test2opt)
177+
err = sn.Commit(ctx2, key2, key2t, test2opt)
130178
if err != nil {
131179
t.Fatal(err)
132180
}
133181

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

189+
ctx2, leased2 := snapshotLease(ctx2, t, db, snapshotter)
190+
if leased2(key2) {
191+
t.Errorf("new lease should not have previously created snapshots")
192+
}
141193
// This should error out, already exists in namespace
142194
// despite mismatched parent
143-
_, err = sn.Prepare(ctx2, "test2-tmp-again", "", test2opt)
195+
key2ta := "test2-tmp-again"
196+
_, err = sn.Prepare(ctx2, key2ta, "", test2opt)
144197
if err == nil {
145198
t.Fatal("expected already exists error")
146199
} else if !errdefs.IsAlreadyExists(err) {
147200
t.Fatal(err)
148201
}
202+
if !leased2(key2) {
203+
t.Errorf("no lease for %q", key2)
204+
}
149205

150206
// In original namespace, but already exists
151-
_, err = sn.Prepare(ctx, "test2-tmp-again", "test1", test2opt)
207+
_, err = sn.Prepare(ctx, key2ta, key1, test2opt)
152208
if err == nil {
153209
t.Fatal("expected already exists error")
154210
} else if !errdefs.IsAlreadyExists(err) {
155211
t.Fatal(err)
156212
}
213+
if leased1(key2ta) {
214+
t.Errorf("should not have lease for non-existent snapshot %q", key2ta)
215+
}
157216

158217
// Now try a third namespace
159218

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

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

168228
// Remove, not going to use yet
169-
err = sn.Remove(ctx3, "test2-tmp")
229+
err = sn.Remove(ctx3, key2t)
170230
if err != nil {
171231
t.Fatal(err)
172232
}
173233

174-
_, err = sn.Prepare(ctx3, "test2-tmp", "test1", test2opt)
234+
_, err = sn.Prepare(ctx3, key2t, key1, test2opt)
175235
if err == nil {
176236
t.Fatal("expected not error")
177237
} else if !errdefs.IsNotFound(err) {
178238
t.Fatal(err)
179239
}
240+
if leased3(key1) {
241+
t.Errorf("lease for %q should not have been created", key1)
242+
}
180243

181-
_, err = sn.Prepare(ctx3, "test1-tmp", "", test1opt)
244+
_, err = sn.Prepare(ctx3, key1t, "", test1opt)
182245
if err == nil {
183246
t.Fatal("expected already exists error")
184247
} else if !errdefs.IsAlreadyExists(err) {
185248
t.Fatal(err)
186249
}
250+
if !leased3(key1) {
251+
t.Errorf("no lease for %q", key1)
252+
}
187253

188254
_, err = sn.Prepare(ctx3, "test2-tmp", "test1", test2opt)
189255
if err == nil {
190256
t.Fatal("expected already exists error")
191257
} else if !errdefs.IsAlreadyExists(err) {
192258
t.Fatal(err)
193259
}
260+
if !leased3(key2) {
261+
t.Errorf("no lease for %q", key2)
262+
}
194263
}
195264

196265
func TestFilterInheritedLabels(t *testing.T) {

0 commit comments

Comments
 (0)