Skip to content

Commit ead3445

Browse files
authored
[26.02] Fix AccessInfo clobbering in concurrent core
1 parent 7731500 commit ead3445

2 files changed

Lines changed: 84 additions & 2 deletions

File tree

core/concurrent_core.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3734,6 +3734,13 @@ func (o *ConcurrentTridentOrchestrator) publishVolume(ctx context.Context, volum
37343734
}
37353735
}
37363736

3737+
// Save CSI-provided values before the VolumeAccessInfo overwrite clobbers them.
3738+
// VolumeAccessInfo is embedded in VolumePublishInfo, so assigning it replaces
3739+
// AccessMode and ReadOnly (set from the CSI request) with the volume's stored
3740+
// values (typically zero/false for NAS volumes).
3741+
accessMode := publishInfo.AccessMode
3742+
readOnly := publishInfo.ReadOnly
3743+
37373744
// Fill in what we already know
37383745
publishInfo.VolumeAccessInfo = volume.Config.AccessInfo
37393746
publishInfo.Nodes = results[0].Nodes
@@ -3745,13 +3752,16 @@ func (o *ConcurrentTridentOrchestrator) publishVolume(ctx context.Context, volum
37453752
Name: models.GenerateVolumePublishName(volumeName, nodeName),
37463753
VolumeName: volumeName,
37473754
NodeName: nodeName,
3748-
ReadOnly: publishInfo.ReadOnly,
3749-
AccessMode: publishInfo.AccessMode,
3755+
ReadOnly: readOnly,
3756+
AccessMode: accessMode,
37503757
AutogrowPolicy: volume.Config.RequestedAutogrowPolicy,
37513758
AutogrowIneligible: isVolumeAutogrowIneligible(volume.Config),
37523759
StorageClass: volume.Config.StorageClass,
37533760
BackendUUID: backend.BackendUUID(),
37543761
Pool: volume.Pool,
3762+
Labels: map[string]string{
3763+
config.TridentNodeNameLabel: nodeName,
3764+
},
37553765
}
37563766
if err := o.storeClient.AddVolumePublication(ctx, vp); err != nil {
37573767
// Handle idempotent publish - if publication already exists, treat as success.

core/concurrent_core_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12746,6 +12746,78 @@ func TestPublishVolumeConcurrentCore_PublicationAlreadyExists(t *testing.T) {
1274612746
assert.NoError(t, err, "PublishVolume should succeed even when publication already exists")
1274712747
}
1274812748

12749+
// TestPublishVolume_AccessModeClobbered_ConcurrentCore documents and verifies the bug where
12750+
// publishInfo.VolumeAccessInfo = volume.Config.AccessInfo overwrites the CSI-provided AccessMode
12751+
// and ReadOnly fields because VolumeAccessInfo is embedded in VolumePublishInfo.
12752+
// The concurrent core creates the VolumePublication AFTER this overwrite (unlike the orchestrator
12753+
// core which creates it before), so the VP ends up with zeroed AccessMode and ReadOnly.
12754+
// The test also verifies that Labels are correctly set on the VolumePublication.
12755+
func TestPublishVolume_AccessModeClobbered_ConcurrentCore(t *testing.T) {
12756+
config.CurrentDriverContext = config.ContextCSI
12757+
mockCtrl := gomock.NewController(t)
12758+
defer mockCtrl.Finish()
12759+
db.Initialize()
12760+
12761+
mockStoreClient := mockpersistentstore.NewMockStoreClient(mockCtrl)
12762+
12763+
o := getConcurrentOrchestrator()
12764+
o.storeClient = mockStoreClient
12765+
12766+
driver := mockstorage.NewMockDriver(mockCtrl)
12767+
driver.EXPECT().Name().Return(config.FakeStorageDriverName).AnyTimes()
12768+
driver.EXPECT().GetStorageBackendSpecs(gomock.Any(), gomock.Any()).Return(nil)
12769+
driver.EXPECT().CreateFollowup(gomock.Any(), gomock.Any()).Return(nil)
12770+
driver.EXPECT().Publish(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
12771+
12772+
fakeNode := getFakeNode("testNode")
12773+
addNodesToCache(t, fakeNode)
12774+
fakeBackend := getFakeBackend("testBackend", "uuid", driver)
12775+
addBackendsToCache(t, fakeBackend)
12776+
12777+
// Volume with AccessInfo that has ZERO AccessMode and ReadOnly=false (the default).
12778+
// This simulates a real NAS volume where AccessInfo.AccessMode is never explicitly set.
12779+
fakeVolume := getFakeVolume("testVolume", "uuid")
12780+
fakeVolume.Config.AccessInfo = models.VolumeAccessInfo{
12781+
NfsAccessInfo: models.NfsAccessInfo{NfsServerIP: "10.0.0.1", NfsPath: "/vol1"},
12782+
}
12783+
addVolumesToCache(t, fakeVolume)
12784+
12785+
// Capture the VolumePublication passed to AddVolumePublication
12786+
var capturedVP *models.VolumePublication
12787+
mockStoreClient.EXPECT().AddVolumePublication(gomock.Any(), gomock.Any()).DoAndReturn(
12788+
func(_ context.Context, vp *models.VolumePublication) error {
12789+
capturedVP = vp
12790+
return nil
12791+
})
12792+
mockStoreClient.EXPECT().UpdateVolume(gomock.Any(), gomock.Any()).Return(nil)
12793+
12794+
// CSI request with non-zero AccessMode (SINGLE_NODE_MULTI_WRITER=7) and ReadOnly=true
12795+
const csiAccessMode int32 = 7
12796+
publishInfo := &models.VolumePublishInfo{
12797+
HostName: "testNode",
12798+
}
12799+
publishInfo.ReadOnly = true
12800+
publishInfo.AccessMode = csiAccessMode
12801+
12802+
err := o.PublishVolume(testCtx, "testVolume", publishInfo)
12803+
require.NoError(t, err)
12804+
require.NotNil(t, capturedVP, "AddVolumePublication should have been called")
12805+
12806+
// BUG: The VolumeAccessInfo overwrite at line 3745 clobbers these CSI-provided values.
12807+
// publishInfo.VolumeAccessInfo = volume.Config.AccessInfo replaces the entire embedded struct,
12808+
// zeroing AccessMode (from 7 to 0) and ReadOnly (from true to false).
12809+
assert.Equal(t, csiAccessMode, capturedVP.AccessMode,
12810+
"VP AccessMode should preserve CSI-provided value, not be clobbered by volume.Config.AccessInfo")
12811+
assert.True(t, capturedVP.ReadOnly,
12812+
"VP ReadOnly should preserve CSI-provided value, not be clobbered by volume.Config.AccessInfo")
12813+
12814+
// BUG: Labels are not set in the concurrent core, unlike the orchestrator core's
12815+
// generateVolumePublication helper which initializes Labels with TridentNodeNameLabel.
12816+
assert.NotNil(t, capturedVP.Labels, "VP should have Labels set")
12817+
assert.Equal(t, "testNode", capturedVP.Labels[config.TridentNodeNameLabel],
12818+
"VP Labels should include TridentNodeNameLabel")
12819+
}
12820+
1274912821
func TestUnpublishVolumeConcurrentCore(t *testing.T) {
1275012822
tests := []struct {
1275112823
name string

0 commit comments

Comments
 (0)