Skip to content

Commit 7bbc06f

Browse files
committed
storage: check CSIDriver.Spec.VolumeLifecycleModes
Using a "normal" CSI driver for an inline ephemeral volume may have unexpected and potentially harmful effects when the driver gets a NodePublishVolume call that it isn't expecting. To prevent that mistake, driver deployments for a driver that supports such volumes must: - deploy a CSIDriver object for the driver - set CSIDriver.Spec.VolumeLifecycleModes such that it contains "ephemeral" The default for that field is "persistent", so existing deployments continue to work and are automatically protected against incorrect usage. For the E2E tests we need a way to specify the driver mode. The existing cluster-driver-registrar doesn't support that and also was deprecated, so we stop using it altogether and instead deploy and patch a CSIDriver object.
1 parent b60f08e commit 7bbc06f

File tree

15 files changed

+522
-310
lines changed

15 files changed

+522
-310
lines changed

pkg/volume/csi/csi_attacher_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,9 @@ func TestAttacherWithCSIDriver(t *testing.T) {
342342
for _, test := range tests {
343343
t.Run(test.name, func(t *testing.T) {
344344
fakeClient := fakeclient.NewSimpleClientset(
345-
getTestCSIDriver("not-attachable", nil, &bFalse),
346-
getTestCSIDriver("attachable", nil, &bTrue),
347-
getTestCSIDriver("nil", nil, nil),
345+
getTestCSIDriver("not-attachable", nil, &bFalse, nil),
346+
getTestCSIDriver("attachable", nil, &bTrue, nil),
347+
getTestCSIDriver("nil", nil, nil, nil),
348348
)
349349
plug, fakeWatcher, tmpDir, _ := newTestWatchPlugin(t, fakeClient)
350350
defer os.RemoveAll(tmpDir)
@@ -430,9 +430,9 @@ func TestAttacherWaitForVolumeAttachmentWithCSIDriver(t *testing.T) {
430430
for _, test := range tests {
431431
t.Run(test.name, func(t *testing.T) {
432432
fakeClient := fakeclient.NewSimpleClientset(
433-
getTestCSIDriver("not-attachable", nil, &bFalse),
434-
getTestCSIDriver("attachable", nil, &bTrue),
435-
getTestCSIDriver("nil", nil, nil),
433+
getTestCSIDriver("not-attachable", nil, &bFalse, nil),
434+
getTestCSIDriver("attachable", nil, &bTrue, nil),
435+
getTestCSIDriver("nil", nil, nil, nil),
436436
)
437437
plug, tmpDir := newTestPlugin(t, fakeClient)
438438
defer os.RemoveAll(tmpDir)

pkg/volume/csi/csi_mounter.go

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"k8s.io/klog"
3030

3131
api "k8s.io/api/core/v1"
32+
storage "k8s.io/api/storage/v1beta1"
3233
apierrs "k8s.io/apimachinery/pkg/api/errors"
3334
"k8s.io/apimachinery/pkg/types"
3435
utilfeature "k8s.io/apiserver/pkg/util/feature"
@@ -46,32 +47,32 @@ var (
4647
driverName,
4748
nodeName,
4849
attachmentID,
49-
csiVolumeMode string
50+
volumeLifecycleMode string
5051
}{
5152
"specVolID",
5253
"volumeHandle",
5354
"driverName",
5455
"nodeName",
5556
"attachmentID",
56-
"csiVolumeMode",
57+
"volumeLifecycleMode",
5758
}
5859
)
5960

6061
type csiMountMgr struct {
6162
csiClientGetter
62-
k8s kubernetes.Interface
63-
plugin *csiPlugin
64-
driverName csiDriverName
65-
csiVolumeMode csiVolumeMode
66-
volumeID string
67-
specVolumeID string
68-
readOnly bool
69-
spec *volume.Spec
70-
pod *api.Pod
71-
podUID types.UID
72-
options volume.VolumeOptions
73-
publishContext map[string]string
74-
kubeVolHost volume.KubeletVolumeHost
63+
k8s kubernetes.Interface
64+
plugin *csiPlugin
65+
driverName csiDriverName
66+
volumeLifecycleMode storage.VolumeLifecycleMode
67+
volumeID string
68+
specVolumeID string
69+
readOnly bool
70+
spec *volume.Spec
71+
pod *api.Pod
72+
podUID types.UID
73+
options volume.VolumeOptions
74+
publishContext map[string]string
75+
kubeVolHost volume.KubeletVolumeHost
7576
volume.MetricsProvider
7677
}
7778

@@ -145,8 +146,8 @@ func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error
145146
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
146147
return fmt.Errorf("CSIInlineVolume feature required")
147148
}
148-
if c.csiVolumeMode != ephemeralVolumeMode {
149-
return fmt.Errorf("unexpected volume mode: %s", c.csiVolumeMode)
149+
if c.volumeLifecycleMode != storage.VolumeLifecycleEphemeral {
150+
return fmt.Errorf("unexpected volume mode: %s", c.volumeLifecycleMode)
150151
}
151152
if volSrc.FSType != nil {
152153
fsType = *volSrc.FSType
@@ -160,8 +161,8 @@ func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error
160161
secretRef = &api.SecretReference{Name: secretName, Namespace: ns}
161162
}
162163
case pvSrc != nil:
163-
if c.csiVolumeMode != persistentVolumeMode {
164-
return fmt.Errorf("unexpected driver mode: %s", c.csiVolumeMode)
164+
if c.volumeLifecycleMode != storage.VolumeLifecyclePersistent {
165+
return fmt.Errorf("unexpected driver mode: %s", c.volumeLifecycleMode)
165166
}
166167

167168
fsType = pvSrc.FSType
@@ -319,7 +320,7 @@ func (c *csiMountMgr) podAttributes() (map[string]string, error) {
319320
"csi.storage.k8s.io/serviceAccount.name": c.pod.Spec.ServiceAccountName,
320321
}
321322
if utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
322-
attrs["csi.storage.k8s.io/ephemeral"] = strconv.FormatBool(c.csiVolumeMode == ephemeralVolumeMode)
323+
attrs["csi.storage.k8s.io/ephemeral"] = strconv.FormatBool(c.volumeLifecycleMode == storage.VolumeLifecycleEphemeral)
323324
}
324325

325326
klog.V(4).Infof(log("CSIDriver %q requires pod information", c.driverName))

pkg/volume/csi/csi_mounter_test.go

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
api "k8s.io/api/core/v1"
3030
storage "k8s.io/api/storage/v1"
31+
storagev1beta1 "k8s.io/api/storage/v1beta1"
3132
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
3233
"k8s.io/apimachinery/pkg/types"
3334
utilfeature "k8s.io/apiserver/pkg/util/feature"
@@ -151,13 +152,16 @@ func MounterSetUpTests(t *testing.T, podInfoEnabled bool) {
151152
for _, test := range tests {
152153
t.Run(test.name, func(t *testing.T) {
153154
klog.Infof("Starting test %s", test.name)
155+
// Modes must be set if (and only if) CSIInlineVolume is enabled.
156+
var modes []storagev1beta1.VolumeLifecycleMode
154157
if test.csiInlineVolume {
155158
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
159+
modes = append(modes, storagev1beta1.VolumeLifecyclePersistent)
156160
}
157161
fakeClient := fakeclient.NewSimpleClientset(
158-
getTestCSIDriver("no-info", &noPodMountInfo, nil),
159-
getTestCSIDriver("info", &currentPodInfoMount, nil),
160-
getTestCSIDriver("nil", nil, nil),
162+
getTestCSIDriver("no-info", &noPodMountInfo, nil, modes),
163+
getTestCSIDriver("info", &currentPodInfoMount, nil, modes),
164+
getTestCSIDriver("nil", nil, nil, modes),
161165
)
162166
plug, tmpDir := newTestPlugin(t, fakeClient)
163167
defer os.RemoveAll(tmpDir)
@@ -278,16 +282,16 @@ func TestMounterSetUpSimple(t *testing.T) {
278282
testCases := []struct {
279283
name string
280284
podUID types.UID
281-
mode csiVolumeMode
285+
mode storagev1beta1.VolumeLifecycleMode
282286
fsType string
283287
options []string
284288
spec func(string, []string) *volume.Spec
285289
shouldFail bool
286290
}{
287291
{
288-
name: "setup with vol source",
292+
name: "setup with ephemeral source",
289293
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
290-
mode: ephemeralVolumeMode,
294+
mode: storagev1beta1.VolumeLifecycleEphemeral,
291295
fsType: "ext4",
292296
shouldFail: true,
293297
spec: func(fsType string, options []string) *volume.Spec {
@@ -299,7 +303,7 @@ func TestMounterSetUpSimple(t *testing.T) {
299303
{
300304
name: "setup with persistent source",
301305
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
302-
mode: persistentVolumeMode,
306+
mode: storagev1beta1.VolumeLifecyclePersistent,
303307
fsType: "zfs",
304308
spec: func(fsType string, options []string) *volume.Spec {
305309
pvSrc := makeTestPV("pv1", 20, testDriver, "vol1")
@@ -311,7 +315,7 @@ func TestMounterSetUpSimple(t *testing.T) {
311315
{
312316
name: "setup with persistent source without unspecified fstype and options",
313317
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
314-
mode: persistentVolumeMode,
318+
mode: storagev1beta1.VolumeLifecyclePersistent,
315319
spec: func(fsType string, options []string) *volume.Spec {
316320
return volume.NewSpecFromPersistentVolume(makeTestPV("pv1", 20, testDriver, "vol2"), false)
317321
},
@@ -345,8 +349,8 @@ func TestMounterSetUpSimple(t *testing.T) {
345349
csiMounter := mounter.(*csiMountMgr)
346350
csiMounter.csiClient = setupClient(t, true)
347351

348-
if csiMounter.csiVolumeMode != persistentVolumeMode {
349-
t.Fatal("unexpected volume mode: ", csiMounter.csiVolumeMode)
352+
if csiMounter.volumeLifecycleMode != storagev1beta1.VolumeLifecyclePersistent {
353+
t.Fatal("unexpected volume mode: ", csiMounter.volumeLifecycleMode)
350354
}
351355

352356
attachID := getAttachmentName(csiMounter.volumeID, string(csiMounter.driverName), string(plug.host.GetNodeName()))
@@ -397,14 +401,10 @@ func TestMounterSetUpSimple(t *testing.T) {
397401
func TestMounterSetUpWithInline(t *testing.T) {
398402
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
399403

400-
fakeClient := fakeclient.NewSimpleClientset()
401-
plug, tmpDir := newTestPlugin(t, fakeClient)
402-
defer os.RemoveAll(tmpDir)
403-
404404
testCases := []struct {
405405
name string
406406
podUID types.UID
407-
mode csiVolumeMode
407+
mode storagev1beta1.VolumeLifecycleMode
408408
fsType string
409409
options []string
410410
spec func(string, []string) *volume.Spec
@@ -413,7 +413,7 @@ func TestMounterSetUpWithInline(t *testing.T) {
413413
{
414414
name: "setup with vol source",
415415
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
416-
mode: ephemeralVolumeMode,
416+
mode: storagev1beta1.VolumeLifecycleEphemeral,
417417
fsType: "ext4",
418418
spec: func(fsType string, options []string) *volume.Spec {
419419
volSrc := makeTestVol("pv1", testDriver)
@@ -424,7 +424,7 @@ func TestMounterSetUpWithInline(t *testing.T) {
424424
{
425425
name: "setup with persistent source",
426426
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
427-
mode: persistentVolumeMode,
427+
mode: storagev1beta1.VolumeLifecyclePersistent,
428428
fsType: "zfs",
429429
spec: func(fsType string, options []string) *volume.Spec {
430430
pvSrc := makeTestPV("pv1", 20, testDriver, "vol1")
@@ -436,7 +436,7 @@ func TestMounterSetUpWithInline(t *testing.T) {
436436
{
437437
name: "setup with persistent source without unspecified fstype and options",
438438
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
439-
mode: persistentVolumeMode,
439+
mode: storagev1beta1.VolumeLifecyclePersistent,
440440
spec: func(fsType string, options []string) *volume.Spec {
441441
return volume.NewSpecFromPersistentVolume(makeTestPV("pv1", 20, testDriver, "vol2"), false)
442442
},
@@ -449,6 +449,15 @@ func TestMounterSetUpWithInline(t *testing.T) {
449449
}
450450

451451
for _, tc := range testCases {
452+
// The fake driver currently supports all modes.
453+
volumeLifecycleModes := []storagev1beta1.VolumeLifecycleMode{
454+
storagev1beta1.VolumeLifecycleEphemeral,
455+
storagev1beta1.VolumeLifecyclePersistent,
456+
}
457+
driver := getTestCSIDriver(testDriver, nil, nil, volumeLifecycleModes)
458+
fakeClient := fakeclient.NewSimpleClientset(driver)
459+
plug, tmpDir := newTestPlugin(t, fakeClient)
460+
defer os.RemoveAll(tmpDir)
452461
registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
453462
t.Run(tc.name, func(t *testing.T) {
454463
mounter, err := plug.NewMounter(
@@ -470,15 +479,15 @@ func TestMounterSetUpWithInline(t *testing.T) {
470479
csiMounter := mounter.(*csiMountMgr)
471480
csiMounter.csiClient = setupClient(t, true)
472481

473-
if csiMounter.csiVolumeMode != tc.mode {
474-
t.Fatal("unexpected volume mode: ", csiMounter.csiVolumeMode)
482+
if csiMounter.volumeLifecycleMode != tc.mode {
483+
t.Fatal("unexpected volume mode: ", csiMounter.volumeLifecycleMode)
475484
}
476485

477-
if csiMounter.csiVolumeMode == ephemeralVolumeMode && csiMounter.volumeID != makeVolumeHandle(string(tc.podUID), csiMounter.specVolumeID) {
486+
if csiMounter.volumeLifecycleMode == storagev1beta1.VolumeLifecycleEphemeral && csiMounter.volumeID != makeVolumeHandle(string(tc.podUID), csiMounter.specVolumeID) {
478487
t.Fatal("unexpected generated volumeHandle:", csiMounter.volumeID)
479488
}
480489

481-
if csiMounter.csiVolumeMode == persistentVolumeMode {
490+
if csiMounter.volumeLifecycleMode == storagev1beta1.VolumeLifecyclePersistent {
482491
attachID := getAttachmentName(csiMounter.volumeID, string(csiMounter.driverName), string(plug.host.GetNodeName()))
483492
attachment := makeTestAttachment(attachID, "test-node", csiMounter.spec.Name())
484493
_, err = csiMounter.k8s.StorageV1().VolumeAttachments().Create(attachment)
@@ -503,10 +512,10 @@ func TestMounterSetUpWithInline(t *testing.T) {
503512
}
504513

505514
// validate stagingTargetPath
506-
if tc.mode == ephemeralVolumeMode && vol.DeviceMountPath != "" {
515+
if tc.mode == storagev1beta1.VolumeLifecycleEphemeral && vol.DeviceMountPath != "" {
507516
t.Errorf("unexpected devicePathTarget sent to driver: %s", vol.DeviceMountPath)
508517
}
509-
if tc.mode == persistentVolumeMode {
518+
if tc.mode == storagev1beta1.VolumeLifecyclePersistent {
510519
devicePath, err := makeDeviceMountPath(plug, csiMounter.spec)
511520
if err != nil {
512521
t.Fatal(err)

0 commit comments

Comments
 (0)