Skip to content

Commit 950d13a

Browse files
authored
Concurrent delete node fix
1 parent e0526bf commit 950d13a

2 files changed

Lines changed: 94 additions & 13 deletions

File tree

core/concurrent_core.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5193,7 +5193,7 @@ func (o *ConcurrentTridentOrchestrator) DeleteNode(ctx context.Context, nodeName
51935193

51945194
results, unlocker, err := db.Lock(
51955195
ctx,
5196-
db.Query(db.ListVolumePublications(), db.DeleteNode(nodeName)),
5196+
db.Query(db.ListVolumePublicationsForNode(nodeName), db.DeleteNode(nodeName)),
51975197
db.Query(db.UpsertNode(nodeName)),
51985198
)
51995199
if err != nil {
@@ -5203,27 +5203,25 @@ func (o *ConcurrentTridentOrchestrator) DeleteNode(ctx context.Context, nodeName
52035203
volumePublications := results[0].VolumePublications
52045204
node := results[0].Node.Read
52055205
deleteNode := results[0].Node.Delete
5206-
upsertNode := results[0].Node.Upsert
5206+
upsertNode := results[1].Node.Upsert
52075207

52085208
if node == nil {
52095209
return errors.NotFoundError("node %v was not found", nodeName)
52105210
}
52115211

52125212
// If there are still volumes published to this node, this is a sudden node removal. Preserve the node CR and mark
52135213
// it as deleted so we can handle the eventual unpublish calls for the affected volumes.
5214-
for _, pub := range volumePublications {
5215-
if pub.NodeName == nodeName {
5216-
Logc(ctx).WithField("node", nodeName).Debug(
5217-
"There are still volumes published to this node, marking node CR as deleted.")
5218-
node.Deleted = true
5219-
if err = o.storeClient.AddOrUpdateNode(ctx, node); err != nil {
5220-
unlocker()
5221-
return
5222-
}
5223-
upsertNode(node)
5214+
if len(volumePublications) > 0 {
5215+
Logc(ctx).WithField("node", nodeName).Debug(
5216+
"There are still volumes published to this node, marking node CR as deleted.")
5217+
node.Deleted = true
5218+
if err = o.storeClient.AddOrUpdateNode(ctx, node); err != nil {
52245219
unlocker()
52255220
return
52265221
}
5222+
upsertNode(node)
5223+
unlocker()
5224+
return
52275225
}
52285226

52295227
// No publications for this node, so we can delete it.

core/concurrent_core_test.go

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
var (
3737
inMemoryPersistence *persistentstore.InMemoryClient
3838
testCtx = context.Background()
39+
expiredCtx, _ = context.WithDeadline(context.Background(), time.Now().Add(-time.Hour))
3940
failed = errors.New("failed")
4041
)
4142

@@ -10146,6 +10147,7 @@ func TestDeleteNodeConcurrentCore(t *testing.T) {
1014610147
name string
1014710148
nodeName string
1014810149
bootstrapError error
10150+
ctx context.Context
1014910151
setupMocks func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator)
1015010152
verifyError func(err error)
1015110153
verifyBehavior func(t *testing.T, o *ConcurrentTridentOrchestrator)
@@ -10154,6 +10156,7 @@ func TestDeleteNodeConcurrentCore(t *testing.T) {
1015410156
name: "Success",
1015510157
nodeName: "testNode",
1015610158
bootstrapError: nil,
10159+
ctx: testCtx,
1015710160
setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) {
1015810161
node := &models.Node{Name: "testNode"}
1015910162
addNodesToCache(t, node)
@@ -10187,10 +10190,73 @@ func TestDeleteNodeConcurrentCore(t *testing.T) {
1018710190
assert.Empty(t, nodes)
1018810191
},
1018910192
},
10193+
{
10194+
name: "SuccessNodeHasPublications",
10195+
nodeName: "testNode",
10196+
bootstrapError: nil,
10197+
ctx: testCtx,
10198+
setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) {
10199+
node := &models.Node{Name: "testNode"}
10200+
addNodesToCache(t, node)
10201+
10202+
// Add backends to cache
10203+
mockBackend1 := getMockBackend(mockCtrl, "backend1", "uuid1")
10204+
mockBackend2 := getMockBackend(mockCtrl, "backend2", "uuid2")
10205+
addBackendsToCache(t, mockBackend1, mockBackend2)
10206+
10207+
// Add publication to cache
10208+
fakePublication := getFakeVolumePublication("vol1", "testNode")
10209+
addVolumePublicationsToCache(t, fakePublication)
10210+
10211+
mockStoreClient.EXPECT().AddOrUpdateNode(gomock.Any(), gomock.Any()).Return(nil).Times(1)
10212+
},
10213+
verifyError: func(err error) {
10214+
assert.NoError(t, err)
10215+
},
10216+
verifyBehavior: func(t *testing.T, o *ConcurrentTridentOrchestrator) {
10217+
// Verify node is still present and marked as deleted
10218+
nodes, err := o.ListNodes(context.Background())
10219+
assert.NoError(t, err)
10220+
assert.NotEmpty(t, nodes)
10221+
assert.True(t, *nodes[0].Deleted)
10222+
},
10223+
},
10224+
{
10225+
name: "SuccessNodeHasPublicationsUpdateFailed",
10226+
nodeName: "testNode",
10227+
bootstrapError: nil,
10228+
ctx: testCtx,
10229+
setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) {
10230+
node := &models.Node{Name: "testNode"}
10231+
addNodesToCache(t, node)
10232+
10233+
// Add backends to cache
10234+
mockBackend1 := getMockBackend(mockCtrl, "backend1", "uuid1")
10235+
mockBackend2 := getMockBackend(mockCtrl, "backend2", "uuid2")
10236+
addBackendsToCache(t, mockBackend1, mockBackend2)
10237+
10238+
// Add publication to cache
10239+
fakePublication := getFakeVolumePublication("vol1", "testNode")
10240+
addVolumePublicationsToCache(t, fakePublication)
10241+
10242+
mockStoreClient.EXPECT().AddOrUpdateNode(gomock.Any(), gomock.Any()).Return(failed).Times(1)
10243+
},
10244+
verifyError: func(err error) {
10245+
assert.Error(t, err)
10246+
},
10247+
verifyBehavior: func(t *testing.T, o *ConcurrentTridentOrchestrator) {
10248+
// Verify node is still present and unchanged
10249+
nodes, err := o.ListNodes(context.Background())
10250+
assert.NoError(t, err)
10251+
assert.NotEmpty(t, nodes)
10252+
assert.False(t, *nodes[0].Deleted)
10253+
},
10254+
},
1019010255
{
1019110256
name: "NodeNotFound",
1019210257
nodeName: "nonExistentNode",
1019310258
bootstrapError: nil,
10259+
ctx: testCtx,
1019410260
setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) {
1019510261
// No node added to cache
1019610262

@@ -10210,6 +10276,7 @@ func TestDeleteNodeConcurrentCore(t *testing.T) {
1021010276
name: "DeleteNodeError",
1021110277
nodeName: "testNode",
1021210278
bootstrapError: nil,
10279+
ctx: testCtx,
1021310280
setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) {
1021410281
node := &models.Node{Name: "testNode"}
1021510282
addNodesToCache(t, node)
@@ -10235,6 +10302,7 @@ func TestDeleteNodeConcurrentCore(t *testing.T) {
1023510302
name: "BootstrapError",
1023610303
nodeName: "testNode",
1023710304
bootstrapError: errors.New("bootstrap error"),
10305+
ctx: testCtx,
1023810306
setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) {
1023910307
// No setup needed
1024010308
},
@@ -10245,6 +10313,21 @@ func TestDeleteNodeConcurrentCore(t *testing.T) {
1024510313
// No behavior to verify
1024610314
},
1024710315
},
10316+
{
10317+
name: "LockError",
10318+
nodeName: "testNode",
10319+
bootstrapError: nil,
10320+
ctx: expiredCtx,
10321+
setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) {
10322+
// No setup needed
10323+
},
10324+
verifyError: func(err error) {
10325+
assert.ErrorContains(t, err, "context deadline exceeded")
10326+
},
10327+
verifyBehavior: func(t *testing.T, o *ConcurrentTridentOrchestrator) {
10328+
// No behavior to verify
10329+
},
10330+
},
1024810331
}
1024910332

1025010333
for _, tt := range tests {
@@ -10265,7 +10348,7 @@ func TestDeleteNodeConcurrentCore(t *testing.T) {
1026510348
tt.setupMocks(mockCtrl, mockStoreClient, o)
1026610349
}
1026710350

10268-
err := o.DeleteNode(testCtx, tt.nodeName)
10351+
err := o.DeleteNode(tt.ctx, tt.nodeName)
1026910352

1027010353
if tt.verifyError != nil {
1027110354
tt.verifyError(err)

0 commit comments

Comments
 (0)