Skip to content

Commit d1fcd55

Browse files
authored
Fix AccessInfo clobbering in concurrent core
1 parent 28ba754 commit d1fcd55

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
@@ -3730,6 +3730,13 @@ func (o *ConcurrentTridentOrchestrator) publishVolume(ctx context.Context, volum
37303730
}
37313731
}
37323732

3733+
// Save CSI-provided values before the VolumeAccessInfo overwrite clobbers them.
3734+
// VolumeAccessInfo is embedded in VolumePublishInfo, so assigning it replaces
3735+
// AccessMode and ReadOnly (set from the CSI request) with the volume's stored
3736+
// values (typically zero/false for NAS volumes).
3737+
accessMode := publishInfo.AccessMode
3738+
readOnly := publishInfo.ReadOnly
3739+
37333740
// Fill in what we already know
37343741
publishInfo.VolumeAccessInfo = volume.Config.AccessInfo
37353742
publishInfo.Nodes = results[0].Nodes
@@ -3741,13 +3748,16 @@ func (o *ConcurrentTridentOrchestrator) publishVolume(ctx context.Context, volum
37413748
Name: models.GenerateVolumePublishName(volumeName, nodeName),
37423749
VolumeName: volumeName,
37433750
NodeName: nodeName,
3744-
ReadOnly: publishInfo.ReadOnly,
3745-
AccessMode: publishInfo.AccessMode,
3751+
ReadOnly: readOnly,
3752+
AccessMode: accessMode,
37463753
AutogrowPolicy: volume.Config.RequestedAutogrowPolicy,
37473754
AutogrowIneligible: isVolumeAutogrowIneligible(volume.Config),
37483755
StorageClass: volume.Config.StorageClass,
37493756
BackendUUID: backend.BackendUUID(),
37503757
Pool: volume.Pool,
3758+
Labels: map[string]string{
3759+
config.TridentNodeNameLabel: nodeName,
3760+
},
37513761
}
37523762
if err := o.storeClient.AddVolumePublication(ctx, vp); err != nil {
37533763
// 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
@@ -12727,6 +12727,78 @@ func TestPublishVolumeConcurrentCore_PublicationAlreadyExists(t *testing.T) {
1272712727
assert.NoError(t, err, "PublishVolume should succeed even when publication already exists")
1272812728
}
1272912729

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

0 commit comments

Comments
 (0)