diff --git a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go index 1dc92de1318..313ec5fa0bb 100644 --- a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go +++ b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go @@ -35,7 +35,6 @@ import ( "k8s.io/apimachinery/pkg/util/wait" utilfeature "k8s.io/apiserver/pkg/util/feature" clientset "k8s.io/client-go/kubernetes" - podutil "k8s.io/kubernetes/pkg/api/v1/pod" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/kubelet/config" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" @@ -384,16 +383,6 @@ func (dswp *desiredStateOfWorldPopulator) checkVolumeFSResize( // or online resize in subsequent loop(after we confirm it has been mounted). return } - fsVolume, err := util.CheckVolumeModeFilesystem(volumeSpec) - if err != nil { - klog.Errorf("Check volume mode failed for volume %s(OuterVolumeSpecName %s): %v", - uniqueVolumeName, podVolume.Name, err) - return - } - if !fsVolume { - klog.V(5).Infof("Block mode volume needn't to check file system resize request") - return - } if processedVolumesForFSResize.Has(string(uniqueVolumeName)) { // File system resize operation is a global operation for volume, // so we only need to check it once if more than one pod use it. @@ -415,22 +404,7 @@ func mountedReadOnlyByPod(podVolume v1.Volume, pod *v1.Pod) bool { if podVolume.PersistentVolumeClaim.ReadOnly { return true } - - return podutil.VisitContainers(&pod.Spec, func(c *v1.Container) bool { - if !mountedReadOnlyByContainer(podVolume.Name, c) { - return false - } - return true - }) -} - -func mountedReadOnlyByContainer(volumeName string, container *v1.Container) bool { - for _, volumeMount := range container.VolumeMounts { - if volumeMount.Name == volumeName && !volumeMount.ReadOnly { - return false - } - } - return true + return false } func getUniqueVolumeName( diff --git a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go index 7c8d2f112c5..23727b18bda 100644 --- a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go +++ b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go @@ -568,8 +568,6 @@ func TestCreateVolumeSpec_Invalid_Block_VolumeMounts(t *testing.T) { } func TestCheckVolumeFSResize(t *testing.T) { - mode := v1.PersistentVolumeFilesystem - setCapacity := func(pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim, capacity int) { pv.Spec.Capacity = volumeCapacity(capacity) pvc.Spec.Resources.Requests = volumeCapacity(capacity) @@ -580,6 +578,7 @@ func TestCheckVolumeFSResize(t *testing.T) { verify func(*testing.T, []v1.UniqueVolumeName, v1.UniqueVolumeName) enableResize bool readOnlyVol bool + volumeMode v1.PersistentVolumeMode }{ { // No resize request for volume, volumes in ASW shouldn't be marked as fsResizeRequired @@ -591,6 +590,7 @@ func TestCheckVolumeFSResize(t *testing.T) { } }, enableResize: true, + volumeMode: v1.PersistentVolumeFilesystem, }, { // Disable the feature gate, so volume shouldn't be marked as fsResizeRequired @@ -603,6 +603,7 @@ func TestCheckVolumeFSResize(t *testing.T) { } }, enableResize: false, + volumeMode: v1.PersistentVolumeFilesystem, }, { // Make volume used as ReadOnly, so volume shouldn't be marked as fsResizeRequired @@ -616,6 +617,7 @@ func TestCheckVolumeFSResize(t *testing.T) { }, readOnlyVol: true, enableResize: true, + volumeMode: v1.PersistentVolumeFilesystem, }, { // Clear ASW, so volume shouldn't be marked as fsResizeRequired because they are not mounted @@ -629,6 +631,7 @@ func TestCheckVolumeFSResize(t *testing.T) { } }, enableResize: true, + volumeMode: v1.PersistentVolumeFilesystem, }, { // volume in ASW should be marked as fsResizeRequired @@ -647,6 +650,26 @@ func TestCheckVolumeFSResize(t *testing.T) { } }, enableResize: true, + volumeMode: v1.PersistentVolumeFilesystem, + }, + { + // volume in ASW should be marked as fsResizeRequired + resize: func(_ *testing.T, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim, _ *desiredStateOfWorldPopulator) { + setCapacity(pv, pvc, 2) + }, + verify: func(t *testing.T, vols []v1.UniqueVolumeName, volName v1.UniqueVolumeName) { + if len(vols) == 0 { + t.Fatalf("Request resize for volume, but volume in ASW hasn't been marked as fsResizeRequired") + } + if len(vols) != 1 { + t.Errorf("Some unexpected volumes are marked as fsResizeRequired: %v", vols) + } + if vols[0] != volName { + t.Fatalf("Mark wrong volume as fsResizeRequired: %s", vols[0]) + } + }, + enableResize: true, + volumeMode: v1.PersistentVolumeBlock, }, } @@ -659,7 +682,7 @@ func TestCheckVolumeFSResize(t *testing.T) { PersistentVolumeSource: v1.PersistentVolumeSource{RBD: &v1.RBDPersistentVolumeSource{}}, Capacity: volumeCapacity(1), ClaimRef: &v1.ObjectReference{Namespace: "ns", Name: "file-bound"}, - VolumeMode: &mode, + VolumeMode: &tc.volumeMode, }, } pvc := &v1.PersistentVolumeClaim{ @@ -677,10 +700,10 @@ func TestCheckVolumeFSResize(t *testing.T) { dswp, fakePodManager, fakeDSW := createDswpWithVolume(t, pv, pvc) fakeASW := dswp.actualStateOfWorld + containers := []v1.Container{} - // create pod - containers := []v1.Container{ - { + if tc.volumeMode == v1.PersistentVolumeFilesystem { + containers = append(containers, v1.Container{ VolumeMounts: []v1.VolumeMount{ { Name: pv.Name, @@ -688,9 +711,20 @@ func TestCheckVolumeFSResize(t *testing.T) { ReadOnly: tc.readOnlyVol, }, }, - }, + }) + } else { + containers = append(containers, v1.Container{ + VolumeDevices: []v1.VolumeDevice{ + { + Name: pv.Name, + DevicePath: "/mnt/foobar", + }, + }, + }) } + pod := createPodWithVolume("dswp-test-pod", "dswp-test-volume-name", "file-bound", containers) + pod.Spec.Volumes[0].VolumeSource.PersistentVolumeClaim.ReadOnly = tc.readOnlyVol uniquePodName := types.UniquePodName(pod.UID) uniqueVolumeName := v1.UniqueVolumeName("fake-plugin/" + pod.Spec.Volumes[0].Name) diff --git a/pkg/volume/awsebs/aws_ebs.go b/pkg/volume/awsebs/aws_ebs.go index 0edde65f15e..5b5bff6f803 100644 --- a/pkg/volume/awsebs/aws_ebs.go +++ b/pkg/volume/awsebs/aws_ebs.go @@ -324,7 +324,15 @@ func (plugin *awsElasticBlockStorePlugin) ExpandVolumeDevice( } func (plugin *awsElasticBlockStorePlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) { - _, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) + fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec) + if err != nil { + return false, fmt.Errorf("error checking VolumeMode: %v", err) + } + // if volume is not a fs file system, there is nothing for us to do here. + if !fsVolume { + return true, nil + } + _, err = util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) if err != nil { return false, err } diff --git a/pkg/volume/azure_dd/azure_dd.go b/pkg/volume/azure_dd/azure_dd.go index 53232c45277..3fe044693d1 100644 --- a/pkg/volume/azure_dd/azure_dd.go +++ b/pkg/volume/azure_dd/azure_dd.go @@ -298,7 +298,15 @@ func (plugin *azureDataDiskPlugin) ExpandVolumeDevice( } func (plugin *azureDataDiskPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) { - _, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) + fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec) + if err != nil { + return false, fmt.Errorf("error checking VolumeMode: %v", err) + } + // if volume is not a fs file system, there is nothing for us to do here. + if !fsVolume { + return true, nil + } + _, err = util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) if err != nil { return false, err } diff --git a/pkg/volume/cinder/cinder.go b/pkg/volume/cinder/cinder.go index b7f9d386653..c94060af7a9 100644 --- a/pkg/volume/cinder/cinder.go +++ b/pkg/volume/cinder/cinder.go @@ -311,7 +311,16 @@ func (plugin *cinderPlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resour } func (plugin *cinderPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) { - _, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) + fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec) + if err != nil { + return false, fmt.Errorf("error checking VolumeMode: %v", err) + } + // if volume is not a fs file system, there is nothing for us to do here. + if !fsVolume { + return true, nil + } + + _, err = util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) if err != nil { return false, err } diff --git a/pkg/volume/flexvolume/expander-defaults.go b/pkg/volume/flexvolume/expander-defaults.go index ec4fd29f748..1699a0844ca 100644 --- a/pkg/volume/flexvolume/expander-defaults.go +++ b/pkg/volume/flexvolume/expander-defaults.go @@ -17,6 +17,8 @@ limitations under the License. package flexvolume import ( + "fmt" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/klog" "k8s.io/kubernetes/pkg/volume" @@ -40,7 +42,16 @@ func (e *expanderDefaults) ExpandVolumeDevice(spec *volume.Spec, newSize resourc // generic filesystem resize func (e *expanderDefaults) NodeExpand(rsOpt volume.NodeResizeOptions) (bool, error) { klog.Warning(logPrefix(e.plugin), "using default filesystem resize for volume ", rsOpt.VolumeSpec.Name(), ", at ", rsOpt.DevicePath) - _, err := util.GenericResizeFS(e.plugin.host, e.plugin.GetPluginName(), rsOpt.DevicePath, rsOpt.DeviceMountPath) + fsVolume, err := util.CheckVolumeModeFilesystem(rsOpt.VolumeSpec) + if err != nil { + return false, fmt.Errorf("error checking VolumeMode: %v", err) + } + // if volume is not a fs file system, there is nothing for us to do here. + if !fsVolume { + return true, nil + } + + _, err = util.GenericResizeFS(e.plugin.host, e.plugin.GetPluginName(), rsOpt.DevicePath, rsOpt.DeviceMountPath) if err != nil { return false, err } diff --git a/pkg/volume/gcepd/gce_pd.go b/pkg/volume/gcepd/gce_pd.go index 2f459bf8304..4761a022482 100644 --- a/pkg/volume/gcepd/gce_pd.go +++ b/pkg/volume/gcepd/gce_pd.go @@ -280,7 +280,15 @@ func (plugin *gcePersistentDiskPlugin) ExpandVolumeDevice( } func (plugin *gcePersistentDiskPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) { - _, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) + fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec) + if err != nil { + return false, fmt.Errorf("error checking VolumeMode: %v", err) + } + // if volume is not a fs file system, there is nothing for us to do here. + if !fsVolume { + return true, nil + } + _, err = util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) if err != nil { return false, err } diff --git a/pkg/volume/rbd/rbd.go b/pkg/volume/rbd/rbd.go index a0ee94949bf..cd953db711c 100644 --- a/pkg/volume/rbd/rbd.go +++ b/pkg/volume/rbd/rbd.go @@ -35,6 +35,7 @@ import ( "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" + "k8s.io/kubernetes/pkg/volume/util" volutil "k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util/volumepathhandler" utilstrings "k8s.io/utils/strings" @@ -202,7 +203,15 @@ func (plugin *rbdPlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resource. } func (plugin *rbdPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) { - _, err := volutil.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) + fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec) + if err != nil { + return false, fmt.Errorf("error checking VolumeMode: %v", err) + } + // if volume is not a fs file system, there is nothing for us to do here. + if !fsVolume { + return true, nil + } + _, err = volutil.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) if err != nil { return false, err } diff --git a/pkg/volume/util/operationexecutor/BUILD b/pkg/volume/util/operationexecutor/BUILD index b1c2efe37e5..a8e3a6e67f3 100644 --- a/pkg/volume/util/operationexecutor/BUILD +++ b/pkg/volume/util/operationexecutor/BUILD @@ -12,6 +12,7 @@ go_library( "fakegenerator.go", "operation_executor.go", "operation_generator.go", + "volume_operation.go", ], importpath = "k8s.io/kubernetes/pkg/volume/util/operationexecutor", deps = [ diff --git a/pkg/volume/util/operationexecutor/operation_generator.go b/pkg/volume/util/operationexecutor/operation_generator.go index 79245d18afc..604848b0b0c 100644 --- a/pkg/volume/util/operationexecutor/operation_generator.go +++ b/pkg/volume/util/operationexecutor/operation_generator.go @@ -707,12 +707,12 @@ func (og *operationGenerator) GenerateMountVolumeFunc( resizeOptions.DeviceMountPath = deviceMountPath resizeOptions.CSIVolumePhase = volume.CSIVolumeStaged - // resizeFileSystem will resize the file system if user has requested a resize of + // NodeExpandVolume will resize the file system if user has requested a resize of // underlying persistent volume and is allowed to do so. - resizeDone, resizeError = og.resizeFileSystem(volumeToMount, resizeOptions) + resizeDone, resizeError = og.nodeExpandVolume(volumeToMount, resizeOptions) if resizeError != nil { - klog.Errorf("MountVolume.resizeFileSystem failed with %v", resizeError) + klog.Errorf("MountVolume.NodeExpandVolume failed with %v", resizeError) return volumeToMount.GenerateError("MountVolume.MountDevice failed while expanding volume", resizeError) } } @@ -750,9 +750,9 @@ func (og *operationGenerator) GenerateMountVolumeFunc( // - Volume does not support DeviceMounter interface. // - In case of CSI the volume does not have node stage_unstage capability. if !resizeDone { - resizeDone, resizeError = og.resizeFileSystem(volumeToMount, resizeOptions) + resizeDone, resizeError = og.nodeExpandVolume(volumeToMount, resizeOptions) if resizeError != nil { - klog.Errorf("MountVolume.resizeFileSystem failed with %v", resizeError) + klog.Errorf("MountVolume.NodeExpandVolume failed with %v", resizeError) return volumeToMount.GenerateError("MountVolume.Setup failed while expanding volume", resizeError) } } @@ -789,72 +789,6 @@ func (og *operationGenerator) GenerateMountVolumeFunc( } } -func (og *operationGenerator) resizeFileSystem(volumeToMount VolumeToMount, rsOpts volume.NodeResizeOptions) (bool, error) { - if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) { - klog.V(4).Infof("Resizing is not enabled for this volume %s", volumeToMount.VolumeName) - return true, nil - } - - if volumeToMount.VolumeSpec != nil && - volumeToMount.VolumeSpec.InlineVolumeSpecForCSIMigration { - klog.V(4).Infof("This volume %s is a migrated inline volume and is not resizable", volumeToMount.VolumeName) - return true, nil - } - - // Get expander, if possible - expandableVolumePlugin, _ := - og.volumePluginMgr.FindNodeExpandablePluginBySpec(volumeToMount.VolumeSpec) - - if expandableVolumePlugin != nil && - expandableVolumePlugin.RequiresFSResize() && - volumeToMount.VolumeSpec.PersistentVolume != nil { - pv := volumeToMount.VolumeSpec.PersistentVolume - pvc, err := og.kubeClient.CoreV1().PersistentVolumeClaims(pv.Spec.ClaimRef.Namespace).Get(pv.Spec.ClaimRef.Name, metav1.GetOptions{}) - if err != nil { - // Return error rather than leave the file system un-resized, caller will log and retry - return false, fmt.Errorf("MountVolume.resizeFileSystem get PVC failed : %v", err) - } - - pvcStatusCap := pvc.Status.Capacity[v1.ResourceStorage] - pvSpecCap := pv.Spec.Capacity[v1.ResourceStorage] - if pvcStatusCap.Cmp(pvSpecCap) < 0 { - // File system resize was requested, proceed - klog.V(4).Infof(volumeToMount.GenerateMsgDetailed("MountVolume.resizeFileSystem entering", fmt.Sprintf("DevicePath %q", volumeToMount.DevicePath))) - - if volumeToMount.VolumeSpec.ReadOnly { - simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MountVolume.resizeFileSystem failed", "requested read-only file system") - klog.Warningf(detailedMsg) - og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.FileSystemResizeFailed, simpleMsg) - return true, nil - } - rsOpts.VolumeSpec = volumeToMount.VolumeSpec - rsOpts.NewSize = pvSpecCap - rsOpts.OldSize = pvcStatusCap - resizeDone, resizeErr := expandableVolumePlugin.NodeExpand(rsOpts) - if resizeErr != nil { - return false, fmt.Errorf("MountVolume.resizeFileSystem failed : %v", resizeErr) - } - // Volume resizing is not done but it did not error out. This could happen if a CSI volume - // does not have node stage_unstage capability but was asked to resize the volume before - // node publish. In which case - we must retry resizing after node publish. - if !resizeDone { - return false, nil - } - simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MountVolume.resizeFileSystem succeeded", "") - og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.FileSystemResizeSuccess, simpleMsg) - klog.Infof(detailedMsg) - // File system resize succeeded, now update the PVC's Capacity to match the PV's - err = util.MarkFSResizeFinished(pvc, pvSpecCap, og.kubeClient) - if err != nil { - // On retry, resizeFileSystem will be called again but do nothing - return false, fmt.Errorf("MountVolume.resizeFileSystem update PVC status failed : %v", err) - } - return true, nil - } - } - return true, nil -} - func (og *operationGenerator) GenerateUnmountVolumeFunc( volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, @@ -1165,6 +1099,16 @@ func (og *operationGenerator) GenerateMapVolumeFunc( og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.SuccessfulMountVolume, simpleMsg) klog.V(verbosity).Infof(detailedMsg) + resizeOptions := volume.NodeResizeOptions{ + DevicePath: devicePath, + CSIVolumePhase: volume.CSIVolumePublished, + } + _, resizeError := og.nodeExpandVolume(volumeToMount, resizeOptions) + if resizeError != nil { + klog.Errorf("MapVolume.NodeExpandVolume failed with %v", resizeError) + return volumeToMount.GenerateError("MapVolume.MarkVolumeAsMounted failed while expanding volume", resizeError) + } + // Update actual state of world markVolMountedErr := actualStateOfWorld.MarkVolumeAsMounted( volumeToMount.PodName, @@ -1623,14 +1567,14 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc( if useCSIPlugin(og.volumePluginMgr, volumeToMount.VolumeSpec) { csiSpec, err := translateSpec(volumeToMount.VolumeSpec) if err != nil { - return volumeToMount.GenerateError("VolumeFSResize.translateSpec failed", err) + return volumeToMount.GenerateError("NodeExpandVolume.translateSpec failed", err) } volumeToMount.VolumeSpec = csiSpec } volumePlugin, err := og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec) if err != nil || volumePlugin == nil { - return volumeToMount.GenerateError("VolumeFSResize.FindPluginBySpec failed", err) + return volumeToMount.GenerateError("NodeExpandVolume.FindPluginBySpec failed", err) } var resizeDone bool @@ -1649,7 +1593,7 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc( resizeOptions.DevicePath = volumeToMount.DevicePath dmp, err := volumeAttacher.GetDeviceMountPath(volumeToMount.VolumeSpec) if err != nil { - return volumeToMount.GenerateError("VolumeFSResize.GetDeviceMountPath failed", err) + return volumeToMount.GenerateError("NodeExpandVolume.GetDeviceMountPath failed", err) } resizeOptions.DeviceMountPath = dmp resizeDone, simpleErr, detailedErr = og.doOnlineExpansion(volumeToMount, actualStateOfWorld, resizeOptions) @@ -1667,7 +1611,7 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc( volumeToMount.Pod, volume.VolumeOptions{}) if newMounterErr != nil { - return volumeToMount.GenerateError("VolumeFSResize.NewMounter initialization failed", newMounterErr) + return volumeToMount.GenerateError("NodeExpandVolume.NewMounter initialization failed", newMounterErr) } resizeOptions.DeviceMountPath = volumeMounter.GetPath() @@ -1681,7 +1625,7 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc( } // This is a placeholder error - we should NEVER reach here. err = fmt.Errorf("volume resizing failed for unknown reason") - return volumeToMount.GenerateError("VolumeFSResize.resizeFileSystem failed to resize volume", err) + return volumeToMount.GenerateError("NodeExpandVolume.NodeExpandVolume failed to resize volume", err) } eventRecorderFunc := func(err *error) { @@ -1705,7 +1649,7 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc( volumePlugin, err := og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec) if err != nil || volumePlugin == nil { - return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("VolumeFSResize.FindPluginBySpec failed", err) + return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("NodeExpandVolume.FindPluginBySpec failed", err) } return volumetypes.GeneratedOperations{ @@ -1719,17 +1663,18 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc( func (og *operationGenerator) doOnlineExpansion(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater, resizeOptions volume.NodeResizeOptions) (bool, error, error) { - resizeDone, err := og.resizeFileSystem(volumeToMount, resizeOptions) + + resizeDone, err := og.nodeExpandVolume(volumeToMount, resizeOptions) if err != nil { - klog.Errorf("VolumeFSResize.resizeFileSystem failed : %v", err) - e1, e2 := volumeToMount.GenerateError("VolumeFSResize.resizeFileSystem failed", err) + klog.Errorf("NodeExpandVolume.NodeExpandVolume failed : %v", err) + e1, e2 := volumeToMount.GenerateError("NodeExpandVolume.NodeExpandVolume failed", err) return false, e1, e2 } if resizeDone { markFSResizedErr := actualStateOfWorld.MarkVolumeAsResized(volumeToMount.PodName, volumeToMount.VolumeName) if markFSResizedErr != nil { // On failure, return error. Caller will log and retry. - e1, e2 := volumeToMount.GenerateError("VolumeFSResize.MarkVolumeAsResized failed", markFSResizedErr) + e1, e2 := volumeToMount.GenerateError("NodeExpandVolume.MarkVolumeAsResized failed", markFSResizedErr) return false, e1, e2 } return true, nil, nil @@ -1737,6 +1682,72 @@ func (og *operationGenerator) doOnlineExpansion(volumeToMount VolumeToMount, return false, nil, nil } +func (og *operationGenerator) nodeExpandVolume(volumeToMount VolumeToMount, rsOpts volume.NodeResizeOptions) (bool, error) { + if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) { + klog.V(4).Infof("Resizing is not enabled for this volume %s", volumeToMount.VolumeName) + return true, nil + } + + if volumeToMount.VolumeSpec != nil && + volumeToMount.VolumeSpec.InlineVolumeSpecForCSIMigration { + klog.V(4).Infof("This volume %s is a migrated inline volume and is not resizable", volumeToMount.VolumeName) + return true, nil + } + + // Get expander, if possible + expandableVolumePlugin, _ := + og.volumePluginMgr.FindNodeExpandablePluginBySpec(volumeToMount.VolumeSpec) + + if expandableVolumePlugin != nil && + expandableVolumePlugin.RequiresFSResize() && + volumeToMount.VolumeSpec.PersistentVolume != nil { + pv := volumeToMount.VolumeSpec.PersistentVolume + pvc, err := og.kubeClient.CoreV1().PersistentVolumeClaims(pv.Spec.ClaimRef.Namespace).Get(pv.Spec.ClaimRef.Name, metav1.GetOptions{}) + if err != nil { + // Return error rather than leave the file system un-resized, caller will log and retry + return false, fmt.Errorf("MountVolume.NodeExpandVolume get PVC failed : %v", err) + } + + pvcStatusCap := pvc.Status.Capacity[v1.ResourceStorage] + pvSpecCap := pv.Spec.Capacity[v1.ResourceStorage] + if pvcStatusCap.Cmp(pvSpecCap) < 0 { + // File system resize was requested, proceed + klog.V(4).Infof(volumeToMount.GenerateMsgDetailed("MountVolume.NodeExpandVolume entering", fmt.Sprintf("DevicePath %q", volumeToMount.DevicePath))) + + if volumeToMount.VolumeSpec.ReadOnly { + simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MountVolume.NodeExpandVolume failed", "requested read-only file system") + klog.Warningf(detailedMsg) + og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.FileSystemResizeFailed, simpleMsg) + return true, nil + } + rsOpts.VolumeSpec = volumeToMount.VolumeSpec + rsOpts.NewSize = pvSpecCap + rsOpts.OldSize = pvcStatusCap + resizeDone, resizeErr := expandableVolumePlugin.NodeExpand(rsOpts) + if resizeErr != nil { + return false, fmt.Errorf("MountVolume.NodeExpandVolume failed : %v", resizeErr) + } + // Volume resizing is not done but it did not error out. This could happen if a CSI volume + // does not have node stage_unstage capability but was asked to resize the volume before + // node publish. In which case - we must retry resizing after node publish. + if !resizeDone { + return false, nil + } + simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MountVolume.NodeExpandVolume succeeded", "") + og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.FileSystemResizeSuccess, simpleMsg) + klog.Infof(detailedMsg) + // File system resize succeeded, now update the PVC's Capacity to match the PV's + err = util.MarkFSResizeFinished(pvc, pvSpecCap, og.kubeClient) + if err != nil { + // On retry, NodeExpandVolume will be called again but do nothing + return false, fmt.Errorf("MountVolume.NodeExpandVolume update PVC status failed : %v", err) + } + return true, nil + } + } + return true, nil +} + func checkMountOptionSupport(og *operationGenerator, volumeToMount VolumeToMount, plugin volume.VolumePlugin) error { mountOptions := util.MountOptionFromSpec(volumeToMount.VolumeSpec)