Skip to content

Commit 6bc6df3

Browse files
nixpanicmergify[bot]
authored andcommitted
nvmeof: validate unknown parameters in ControllerModifyVolume
Add validation for unknown mutable parameters in ControllerModifyVolume to catch configuration errors early. Create a shared nvmeofMutableParams slice to define all valid NVMe-oF mutable parameters in one place, eliminating code duplication. Filter out NVMe-oF-specific mutable parameters before passing the request to the RBD backend. NVMe-oF handles these parameters separately through ControllerModifyVolume. This prevents the RBD backend from receiving parameters it doesn't understand, which would cause validation errors. Remove redundant rbd.HasQoSParams() checks that became dead code after the validation was moved earlier in the flow. Assisted-by: AskBob <askbob@ibm.com> Signed-off-by: Niels de Vos <ndevos@ibm.com>
1 parent 0857691 commit 6bc6df3

2 files changed

Lines changed: 141 additions & 17 deletions

File tree

internal/nvmeof/controller/controllerserver.go

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"maps"
2525
"net"
26+
"slices"
2627
"strconv"
2728

2829
"github.com/container-storage-interface/spec/lib/go/csi"
@@ -40,6 +41,15 @@ import (
4041
"github.com/ceph/ceph-csi/internal/util/log"
4142
)
4243

44+
// nvmeofMutableParams lists all NVMe-oF specific mutable parameters.
45+
var nvmeofMutableParams = []string{
46+
nvmeof.RwIosPerSecond,
47+
nvmeof.RwMbytesPerSecond,
48+
nvmeof.RMbytesPerSecond,
49+
nvmeof.WMbytesPerSecond,
50+
nvmeof.AllowHostNQNs,
51+
}
52+
4353
type Server struct {
4454
csi.UnimplementedControllerServer
4555

@@ -147,9 +157,22 @@ func (cs *Server) CreateVolume(
147157
defer cs.volumeLocks.Release(sourceVolumeID)
148158
}
149159

150-
// Step 1: Create RBD volume through backend. if exists, it is ok.
160+
// Create a modified request without any mutable parameters for RBD
161+
// NVMe-oF handles its own mutable parameters separately
162+
rbdReq := &csi.CreateVolumeRequest{
163+
Name: req.GetName(),
164+
CapacityRange: req.GetCapacityRange(),
165+
VolumeCapabilities: req.GetVolumeCapabilities(),
166+
Parameters: req.GetParameters(),
167+
Secrets: req.GetSecrets(),
168+
VolumeContentSource: req.GetVolumeContentSource(),
169+
AccessibilityRequirements: req.GetAccessibilityRequirements(),
170+
// MutableParameters intentionally not set - NVMe-oF manages these separately
171+
}
172+
173+
// Step 2: Create RBD volume through backend. if exists, it is ok.
151174
// RBD backend automatically handles cloning when VolumeContentSource is present.
152-
res, err := cs.backendServer.CreateVolume(ctx, req)
175+
res, err := cs.backendServer.CreateVolume(ctx, rbdReq)
153176
if err != nil {
154177
log.ErrorLog(ctx, "failed to create RBD volume: %v", err)
155178

@@ -182,7 +205,7 @@ func (cs *Server) CreateVolume(
182205
// can be empty. if it was defined in config-map the rbd csi driver would have set it already
183206
rbdRadosNameSpace := res.GetVolume().GetVolumeContext()["radosNamespace"]
184207

185-
// Step 2: Setup NVMe-oF resources
208+
// Step 4: Setup NVMe-oF resources
186209
var nvmeofData *nvmeof.NVMeoFVolumeData
187210
// Defer: Cleanup NVMe-oF on any error (BEFORE the call!)
188211
defer func() {
@@ -203,13 +226,13 @@ func (cs *Server) CreateVolume(
203226

204227
return nil, status.Errorf(codes.Internal, "NVMe-oF setup failed: %v", err)
205228
}
206-
// step 3: Populate volume context for NodeServer
229+
// Step 5: Populate volume context for NodeServer
207230
err = populateVolumeContext(backend, nvmeofData)
208231
if err != nil {
209232
return nil, status.Errorf(codes.Internal, "failed to populate volume context: %v", err)
210233
}
211234

212-
// Step 4: Store NVMe-oF metadata in the volume context
235+
// Step 6: Store NVMe-oF metadata in the volume context
213236
err = cs.storeNVMeoFMetadata(ctx, req, volumeID, nvmeofData)
214237
if err != nil {
215238
return nil, err // Error already formatted with proper status code
@@ -370,13 +393,15 @@ func (cs *Server) ControllerModifyVolume(
370393
}
371394
defer cs.volumeLocks.Release(volumeID)
372395

373-
// Step 2: Parse QoS parameters from mutable_parameters
374-
hasRBDQoS := rbd.HasQoSParams(params)
375-
if hasRBDQoS {
376-
log.ErrorLog(ctx, "Cannot set RBD QoS parameters on NVMe-oF volumes")
377-
378-
return nil, status.Error(codes.InvalidArgument, "cannot set RBD QoS parameters on NVMe-oF volumes")
396+
// Step 2: Validate that only known parameters are provided
397+
for param := range params {
398+
if !slices.Contains(nvmeofMutableParams, param) {
399+
return nil, status.Errorf(codes.InvalidArgument,
400+
"unknown mutable parameter: %s", param)
401+
}
379402
}
403+
404+
// Step 3: Parse QoS parameters from mutable_parameters
380405
nvmeofQoS, err := nvmeof.NewNVMeoFQosVolumeFromParams(params)
381406
if err != nil {
382407
log.ErrorLog(ctx, "failed to parse NVMe-oF QoS parameters: %v", err)
@@ -488,15 +513,19 @@ func validateCreateVolumeRequest(req *csi.CreateVolumeRequest) error {
488513
if countOfListeners > 0 && networkMask != "" {
489514
return errors.New("must specify either 'listeners' xor 'networkMask', but got both")
490515
}
491-
// Validate QoS parameters - cannot mix RBD and NVMe-oF QoS
492-
mutableParams := req.GetMutableParameters()
493516

494-
// check for RBD QoS parameters in both params and mutableParams
495-
if hasRBDQoS := rbd.HasQoSParams(params); hasRBDQoS {
517+
// Validate QoS parameters - cannot mix RBD and NVMe-oF QoS
518+
// Check for RBD QoS parameters in regular params (not mutableParams - already validated above)
519+
if rbd.HasQoSParams(params) {
496520
return errors.New("setting RBD QoS parameters on NVMe-oF volumes is not supported")
497521
}
498-
if hasRBDQoS := rbd.HasQoSParams(mutableParams); hasRBDQoS {
499-
return errors.New("setting RBD QoS parameters on NVMe-oF volumes is not supported")
522+
523+
// Validate that only known mutable parameters are provided
524+
mutableParams := req.GetMutableParameters()
525+
for param := range mutableParams {
526+
if !slices.Contains(nvmeofMutableParams, param) {
527+
return fmt.Errorf("unknown mutable parameter: %s", param)
528+
}
500529
}
501530

502531
// It take the mutableParams value from the volumeAttributesClassName in the PersistentVolumeClaim yaml.

internal/nvmeof/controller/controllerserver_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,98 @@ func TestGetGatewayConfigFromRequest(t *testing.T) {
172172
})
173173
}
174174
}
175+
176+
func TestValidateCreateVolumeRequest(t *testing.T) {
177+
t.Parallel()
178+
179+
tests := []struct {
180+
name string
181+
req *csi.CreateVolumeRequest
182+
shouldFail bool
183+
expectedErrorMsg string
184+
}{
185+
{
186+
name: "valid request with supported mutable parameters",
187+
req: &csi.CreateVolumeRequest{
188+
Name: "test-volume",
189+
VolumeCapabilities: []*csi.VolumeCapability{
190+
{
191+
AccessMode: &csi.VolumeCapability_AccessMode{
192+
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
193+
},
194+
},
195+
},
196+
Parameters: map[string]string{
197+
"nvmeofGatewayAddress": "127.0.0.1",
198+
"listeners": `[{"hostname":"gateway-1","address":"192.168.1.1","port":4420}]`,
199+
},
200+
MutableParameters: map[string]string{
201+
"rwIosPerSecond": "1000",
202+
"rwMbytesPerSecond": "100",
203+
"rMbytesPerSecond": "50",
204+
"wMbytesPerSecond": "50",
205+
},
206+
},
207+
shouldFail: false,
208+
},
209+
{
210+
name: "invalid request with unsupported mutable parameter",
211+
req: &csi.CreateVolumeRequest{
212+
Name: "test-volume",
213+
VolumeCapabilities: []*csi.VolumeCapability{
214+
{
215+
AccessMode: &csi.VolumeCapability_AccessMode{
216+
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
217+
},
218+
},
219+
},
220+
Parameters: map[string]string{
221+
"nvmeofGatewayAddress": "127.0.0.1",
222+
"listeners": `[{"hostname":"gateway-1","address":"192.168.1.1","port":4420}]`,
223+
},
224+
MutableParameters: map[string]string{
225+
"unsupportedParam": "value",
226+
},
227+
},
228+
shouldFail: true,
229+
expectedErrorMsg: "unknown mutable parameter: unsupportedParam",
230+
},
231+
{
232+
name: "invalid request with RBD QoS parameters",
233+
req: &csi.CreateVolumeRequest{
234+
Name: "test-volume",
235+
VolumeCapabilities: []*csi.VolumeCapability{
236+
{
237+
AccessMode: &csi.VolumeCapability_AccessMode{
238+
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
239+
},
240+
},
241+
},
242+
Parameters: map[string]string{
243+
"nvmeofGatewayAddress": "127.0.0.1",
244+
"listeners": `[{"hostname":"gateway-1","address":"192.168.1.1","port":4420}]`,
245+
"baseReadIops": "1000",
246+
},
247+
},
248+
shouldFail: true,
249+
expectedErrorMsg: "setting RBD QoS parameters on NVMe-oF volumes is not supported",
250+
},
251+
}
252+
253+
for _, test := range tests {
254+
t.Run(test.name, func(t *testing.T) {
255+
t.Parallel()
256+
err := validateCreateVolumeRequest(test.req)
257+
if test.shouldFail {
258+
require.Error(t, err)
259+
if test.expectedErrorMsg != "" {
260+
require.Contains(t, err.Error(), test.expectedErrorMsg)
261+
}
262+
263+
return
264+
}
265+
266+
require.NoError(t, err)
267+
})
268+
}
269+
}

0 commit comments

Comments
 (0)