Update code to use new generic allocatedResourceStatus field

This commit is contained in:
Hemant Kumar 2023-07-10 12:13:25 -04:00
parent 56f6030125
commit e011187114
17 changed files with 444 additions and 179 deletions

View File

@ -99,7 +99,7 @@ func DropDisabledFieldsFromStatus(pvc, oldPVC *core.PersistentVolumeClaim) {
pvc.Status.AllocatedResources = nil pvc.Status.AllocatedResources = nil
} }
if !resizeStatusInUse(oldPVC) { if !resizeStatusInUse(oldPVC) {
pvc.Status.ResizeStatus = nil pvc.Status.AllocatedResourceStatuses = nil
} }
} }
} }
@ -179,7 +179,7 @@ func resizeStatusInUse(oldPVC *core.PersistentVolumeClaim) bool {
if oldPVC == nil { if oldPVC == nil {
return false return false
} }
if oldPVC.Status.ResizeStatus != nil { if oldPVC.Status.AllocatedResourceStatuses != nil {
return true return true
} }
return false return false

View File

@ -436,30 +436,30 @@ func TestDropDisabledFieldsFromStatus(t *testing.T) {
{ {
name: "for:newPVC=hasResizeStatus,oldPVC=nil, featuregate=false should drop field", name: "for:newPVC=hasResizeStatus,oldPVC=nil, featuregate=false should drop field",
feature: false, feature: false,
pvc: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), pvc: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
oldPVC: nil, oldPVC: nil,
expected: getPVC(), expected: getPVC(),
}, },
{ {
name: "for:newPVC=hasResizeStatus,oldPVC=doesnot,featuregate=true; should keep field", name: "for:newPVC=hasResizeStatus,oldPVC=doesnot,featuregate=true; should keep field",
feature: true, feature: true,
pvc: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), pvc: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
oldPVC: getPVC(), oldPVC: getPVC(),
expected: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), expected: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
}, },
{ {
name: "for:newPVC=hasResizeStatus,oldPVC=hasResizeStatus,featuregate=true; should keep field", name: "for:newPVC=hasResizeStatus,oldPVC=hasResizeStatus,featuregate=true; should keep field",
feature: true, feature: true,
pvc: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), pvc: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
oldPVC: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), oldPVC: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
expected: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), expected: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
}, },
{ {
name: "for:newPVC=hasResizeStatus,oldPVC=hasResizeStatus,featuregate=false; should keep field", name: "for:newPVC=hasResizeStatus,oldPVC=hasResizeStatus,featuregate=false; should keep field",
feature: false, feature: false,
pvc: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), pvc: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
oldPVC: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), oldPVC: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
expected: withResizeStatus(core.PersistentVolumeClaimNodeExpansionFailed), expected: withResizeStatus(core.PersistentVolumeClaimNodeResizeFailed),
}, },
} }
@ -490,10 +490,12 @@ func withAllocatedResource(q string) *core.PersistentVolumeClaim {
} }
} }
func withResizeStatus(status core.PersistentVolumeClaimResizeStatus) *core.PersistentVolumeClaim { func withResizeStatus(status core.ClaimResourceStatus) *core.PersistentVolumeClaim {
return &core.PersistentVolumeClaim{ return &core.PersistentVolumeClaim{
Status: core.PersistentVolumeClaimStatus{ Status: core.PersistentVolumeClaimStatus{
ResizeStatus: &status, AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
core.ResourceStorage: status,
},
}, },
} }
} }

View File

@ -515,23 +515,26 @@ const (
) )
// +enum // +enum
type PersistentVolumeClaimResizeStatus string // When a controller receives persistentvolume claim update with ClaimResourceStatus for a resource
// that it does not recognizes, then it should ignore that update and let other controllers
// handle it.
type ClaimResourceStatus string
const ( const (
// When expansion is complete, the empty string is set by resize controller or kubelet.
PersistentVolumeClaimNoExpansionInProgress PersistentVolumeClaimResizeStatus = ""
// State set when resize controller starts expanding the volume in control-plane // State set when resize controller starts expanding the volume in control-plane
PersistentVolumeClaimControllerExpansionInProgress PersistentVolumeClaimResizeStatus = "ControllerExpansionInProgress" PersistentVolumeClaimControllerResizeInProgress ClaimResourceStatus = "ControllerResizeInProgress"
// State set when expansion has failed in resize controller with a terminal error. // State set when expansion has failed in resize controller with a terminal error.
// Transient errors such as timeout should not set this status and should leave ResizeStatus // Transient errors such as timeout should not set this status and should leave allocatedResourceStatus
// unmodified, so as resize controller can resume the volume expansion. // unmodified, so as resize controller can resume the volume expansion.
PersistentVolumeClaimControllerExpansionFailed PersistentVolumeClaimResizeStatus = "ControllerExpansionFailed" PersistentVolumeClaimControllerResizeFailed ClaimResourceStatus = "ControllerResizeFailed"
// State set when resize controller has finished expanding the volume but further expansion is needed on the node. // State set when resize controller has finished expanding the volume but further expansion is needed on the node.
PersistentVolumeClaimNodeExpansionPending PersistentVolumeClaimResizeStatus = "NodeExpansionPending" PersistentVolumeClaimNodeResizePending ClaimResourceStatus = "NodeResizePending"
// State set when kubelet starts expanding the volume. // State set when kubelet starts expanding the volume.
PersistentVolumeClaimNodeExpansionInProgress PersistentVolumeClaimResizeStatus = "NodeExpansionInProgress" PersistentVolumeClaimNodeResizeInProgress ClaimResourceStatus = "NodeResizeInProgress"
// State set when expansion has failed in kubelet with a terminal error. Transient errors don't set NodeExpansionFailed. // State set when expansion has failed in kubelet with a terminal error. Transient errors don't set NodeExpansionFailed.
PersistentVolumeClaimNodeExpansionFailed PersistentVolumeClaimResizeStatus = "NodeExpansionFailed" PersistentVolumeClaimNodeResizeFailed ClaimResourceStatus = "NodeResizeFailed"
) )
// PersistentVolumeClaimCondition represents the current condition of PV claim // PersistentVolumeClaimCondition represents the current condition of PV claim
@ -572,13 +575,19 @@ type PersistentVolumeClaimStatus struct {
// +featureGate=RecoverVolumeExpansionFailure // +featureGate=RecoverVolumeExpansionFailure
// +optional // +optional
AllocatedResources ResourceList AllocatedResources ResourceList
// ResizeStatus stores status of resize operation. // allocatedResourceStatuses stores status of resource being resized for the given PVC.
// ResizeStatus is not set by default but when expansion is complete resizeStatus is set to empty // If Expanding a PVC for more capacity - this field can be one of the following states:
// string by resize controller or kubelet. // - pvc.status.allocatedResourceStatus['storage'] = "ControllerResizeInProgress"
// - pvc.status.allocatedResourceStatus['storage'] = "ControllerResizeFailed"
// - pvc.status.allocatedResourceStatus['storage'] = "NodeResizePending"
// - pvc.status.allocatedResourceStatus['storage'] = "NodeResizeInProgress"
// - pvc.status.allocatedResourceStatus['storage'] = "NodeResizeFailed"
// When this field is not set, it means that no resize operation is in progress for the given PVC.
// This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature. // This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.
// +featureGate=RecoverVolumeExpansionFailure // +featureGate=RecoverVolumeExpansionFailure
// +mapType=granular
// +optional // +optional
ResizeStatus *PersistentVolumeClaimResizeStatus AllocatedResourceStatuses map[ResourceName]ClaimResourceStatus
} }
// PersistentVolumeAccessMode defines various access modes for PV. // PersistentVolumeAccessMode defines various access modes for PV.

View File

@ -2294,12 +2294,29 @@ func validateStorageClassUpgradeFromNil(oldAnnotations map[string]string, oldScN
(!oldAnnotationExist || *newScName == oldAnnotation) /* condition 3 */ (!oldAnnotationExist || *newScName == oldAnnotation) /* condition 3 */
} }
var resizeStatusSet = sets.NewString(string(core.PersistentVolumeClaimNoExpansionInProgress), func validatePersistentVolumeClaimResourceKey(value string, fldPath *field.Path) field.ErrorList {
string(core.PersistentVolumeClaimControllerExpansionInProgress), allErrs := field.ErrorList{}
string(core.PersistentVolumeClaimControllerExpansionFailed), for _, msg := range validation.IsQualifiedName(value) {
string(core.PersistentVolumeClaimNodeExpansionPending), allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
string(core.PersistentVolumeClaimNodeExpansionInProgress), }
string(core.PersistentVolumeClaimNodeExpansionFailed)) if len(allErrs) != 0 {
return allErrs
}
// For native resource names such as - either unprefixed names or with kubernetes.io prefix,
// only allowed value is storage
if helper.IsNativeResource(core.ResourceName(value)) {
if core.ResourceName(value) != core.ResourceStorage {
return append(allErrs, field.NotSupported(fldPath, value, []string{string(core.ResourceStorage)}))
}
}
return allErrs
}
var resizeStatusSet = sets.NewString(string(core.PersistentVolumeClaimControllerResizeInProgress),
string(core.PersistentVolumeClaimControllerResizeFailed),
string(core.PersistentVolumeClaimNodeResizePending),
string(core.PersistentVolumeClaimNodeResizeInProgress),
string(core.PersistentVolumeClaimNodeResizeFailed))
// ValidatePersistentVolumeClaimStatusUpdate validates an update to status of a PersistentVolumeClaim // ValidatePersistentVolumeClaimStatusUpdate validates an update to status of a PersistentVolumeClaim
func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, validationOpts PersistentVolumeClaimSpecValidationOptions) field.ErrorList { func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, validationOpts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
@ -2316,19 +2333,26 @@ func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVo
allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...) allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...)
} }
if validationOpts.EnableRecoverFromExpansionFailure { if validationOpts.EnableRecoverFromExpansionFailure {
resizeStatusPath := field.NewPath("status", "resizeStatus") resizeStatusPath := field.NewPath("status", "allocatedResourceStatus")
if newPvc.Status.ResizeStatus != nil { if newPvc.Status.AllocatedResourceStatuses != nil {
resizeStatus := *newPvc.Status.ResizeStatus resizeStatus := newPvc.Status.AllocatedResourceStatuses
if !resizeStatusSet.Has(string(resizeStatus)) { for k, v := range resizeStatus {
allErrs = append(allErrs, field.NotSupported(resizeStatusPath, resizeStatus, resizeStatusSet.List())) if errs := validatePersistentVolumeClaimResourceKey(k.String(), resizeStatusPath); len(errs) > 0 {
allErrs = append(allErrs, errs...)
}
if !resizeStatusSet.Has(string(v)) {
allErrs = append(allErrs, field.NotSupported(resizeStatusPath, k, resizeStatusSet.List()))
continue
}
} }
} }
allocPath := field.NewPath("status", "allocatedResources") allocPath := field.NewPath("status", "allocatedResources")
for r, qty := range newPvc.Status.AllocatedResources { for r, qty := range newPvc.Status.AllocatedResources {
if r != core.ResourceStorage { if errs := validatePersistentVolumeClaimResourceKey(r.String(), allocPath); len(errs) > 0 {
allErrs = append(allErrs, field.NotSupported(allocPath, r, []string{string(core.ResourceStorage)})) allErrs = append(allErrs, errs...)
continue continue
} }
if errs := validateBasicResource(qty, allocPath.Key(string(r))); len(errs) > 0 { if errs := validateBasicResource(qty, allocPath.Key(string(r))); len(errs) > 0 {
allErrs = append(allErrs, errs...) allErrs = append(allErrs, errs...)
} else { } else {

View File

@ -18305,15 +18305,60 @@ func TestValidatePersistentVolumeClaimStatusUpdate(t *testing.T) {
core.ResourceName(core.ResourceCPU): resource.MustParse("10G"), core.ResourceName(core.ResourceCPU): resource.MustParse("10G"),
}, },
}) })
progressResizeStatus := core.PersistentVolumeClaimControllerExpansionInProgress progressResizeStatus := core.PersistentVolumeClaimControllerResizeInProgress
invalidResizeStatus := core.PersistentVolumeClaimResizeStatus("foo")
invalidResizeStatus := core.ClaimResourceStatus("foo")
validResizeKeyCustom := core.ResourceName("example.com/foo")
invalidNativeResizeKey := core.ResourceName("kubernetes.io/foo")
validResizeStatusPVC := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{ validResizeStatusPVC := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{ AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce, core.ReadWriteOnce,
}, },
}, core.PersistentVolumeClaimStatus{ }, core.PersistentVolumeClaimStatus{
ResizeStatus: &progressResizeStatus, AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
core.ResourceStorage: progressResizeStatus,
},
})
validResizeStatusControllerResizeFailed := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
},
}, core.PersistentVolumeClaimStatus{
AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
core.ResourceStorage: core.PersistentVolumeClaimControllerResizeFailed,
},
})
validNodeResizePending := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
},
}, core.PersistentVolumeClaimStatus{
AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
core.ResourceStorage: core.PersistentVolumeClaimNodeResizePending,
},
})
validNodeResizeInProgress := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
},
}, core.PersistentVolumeClaimStatus{
AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
core.ResourceStorage: core.PersistentVolumeClaimNodeResizeInProgress,
},
})
validNodeResizeFailed := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
},
}, core.PersistentVolumeClaimStatus{
AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
core.ResourceStorage: core.PersistentVolumeClaimNodeResizeFailed,
},
}) })
invalidResizeStatusPVC := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{ invalidResizeStatusPVC := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
@ -18321,7 +18366,69 @@ func TestValidatePersistentVolumeClaimStatusUpdate(t *testing.T) {
core.ReadWriteOnce, core.ReadWriteOnce,
}, },
}, core.PersistentVolumeClaimStatus{ }, core.PersistentVolumeClaimStatus{
ResizeStatus: &invalidResizeStatus, AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
core.ResourceStorage: invalidResizeStatus,
},
})
invalidNativeResizeStatusPVC := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
},
}, core.PersistentVolumeClaimStatus{
AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
invalidNativeResizeKey: core.PersistentVolumeClaimNodeResizePending,
},
})
validExternalResizeStatusPVC := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
},
}, core.PersistentVolumeClaimStatus{
AllocatedResourceStatuses: map[core.ResourceName]core.ClaimResourceStatus{
validResizeKeyCustom: core.PersistentVolumeClaimNodeResizePending,
},
})
invalidNativeResourceAllocatedKey := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
core.ReadOnlyMany,
},
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
},
}, core.PersistentVolumeClaimStatus{
Phase: core.ClaimPending,
Conditions: []core.PersistentVolumeClaimCondition{
{Type: core.PersistentVolumeClaimResizing, Status: core.ConditionTrue},
},
AllocatedResources: core.ResourceList{
invalidNativeResizeKey: resource.MustParse("14G"),
},
})
validExternalAllocatedResource := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
core.ReadOnlyMany,
},
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
},
}, core.PersistentVolumeClaimStatus{
Phase: core.ClaimPending,
Conditions: []core.PersistentVolumeClaimCondition{
{Type: core.PersistentVolumeClaimResizing, Status: core.ConditionTrue},
},
AllocatedResources: core.ResourceList{
validResizeKeyCustom: resource.MustParse("14G"),
},
}) })
scenarios := map[string]struct { scenarios := map[string]struct {
@ -18344,6 +18451,21 @@ func TestValidatePersistentVolumeClaimStatusUpdate(t *testing.T) {
enableResize: true, enableResize: true,
enableRecoverFromExpansion: true, enableRecoverFromExpansion: true,
}, },
"status-update-with-invalid-allocatedResources-native-key-feature-enabled": {
isExpectedFailure: true,
oldClaim: validClaim,
newClaim: invalidNativeResourceAllocatedKey,
enableResize: true,
enableRecoverFromExpansion: true,
},
"status-update-with-valid-allocatedResources-external-key-feature-enabled": {
isExpectedFailure: false,
oldClaim: validClaim,
newClaim: validExternalAllocatedResource,
enableResize: true,
enableRecoverFromExpansion: true,
},
"status-update-with-invalid-allocatedResources-feature-enabled": { "status-update-with-invalid-allocatedResources-feature-enabled": {
isExpectedFailure: true, isExpectedFailure: true,
oldClaim: validClaim, oldClaim: validClaim,
@ -18358,6 +18480,48 @@ func TestValidatePersistentVolumeClaimStatusUpdate(t *testing.T) {
enableResize: true, enableResize: true,
enableRecoverFromExpansion: true, enableRecoverFromExpansion: true,
}, },
"staus-update-with-controller-resize-failed": {
isExpectedFailure: false,
oldClaim: validClaim,
newClaim: validResizeStatusControllerResizeFailed,
enableResize: true,
enableRecoverFromExpansion: true,
},
"staus-update-with-node-resize-pending": {
isExpectedFailure: false,
oldClaim: validClaim,
newClaim: validNodeResizePending,
enableResize: true,
enableRecoverFromExpansion: true,
},
"staus-update-with-node-resize-inprogress": {
isExpectedFailure: false,
oldClaim: validClaim,
newClaim: validNodeResizeInProgress,
enableResize: true,
enableRecoverFromExpansion: true,
},
"staus-update-with-node-resize-failed": {
isExpectedFailure: false,
oldClaim: validClaim,
newClaim: validNodeResizeFailed,
enableResize: true,
enableRecoverFromExpansion: true,
},
"staus-update-with-invalid-native-resource-status-key": {
isExpectedFailure: true,
oldClaim: validClaim,
newClaim: invalidNativeResizeStatusPVC,
enableResize: true,
enableRecoverFromExpansion: true,
},
"staus-update-with-valid-external-resource-status-key": {
isExpectedFailure: false,
oldClaim: validClaim,
newClaim: validExternalResizeStatusPVC,
enableResize: true,
enableRecoverFromExpansion: true,
},
"status-update-with-valid-pvc-resize-status": { "status-update-with-valid-pvc-resize-status": {
isExpectedFailure: false, isExpectedFailure: false,
oldClaim: validClaim, oldClaim: validClaim,

View File

@ -587,7 +587,7 @@ func (dsw *desiredStateOfWorld) GetVolumesToMount() []VolumeToMount {
}, },
} }
if volumeObj.persistentVolumeSize != nil { if volumeObj.persistentVolumeSize != nil {
vmt.PersistentVolumeSize = volumeObj.persistentVolumeSize.DeepCopy() vmt.DesiredPersistentVolumeSize = volumeObj.persistentVolumeSize.DeepCopy()
} }
volumesToMount = append(volumesToMount, vmt) volumesToMount = append(volumesToMount, vmt)
} }

View File

@ -178,7 +178,7 @@ func (rc *reconciler) unmountVolumes() {
func (rc *reconciler) mountOrAttachVolumes() { func (rc *reconciler) mountOrAttachVolumes() {
// Ensure volumes that should be attached/mounted are attached/mounted. // Ensure volumes that should be attached/mounted are attached/mounted.
for _, volumeToMount := range rc.desiredStateOfWorld.GetVolumesToMount() { for _, volumeToMount := range rc.desiredStateOfWorld.GetVolumesToMount() {
volMounted, devicePath, err := rc.actualStateOfWorld.PodExistsInVolume(volumeToMount.PodName, volumeToMount.VolumeName, volumeToMount.PersistentVolumeSize, volumeToMount.SELinuxLabel) volMounted, devicePath, err := rc.actualStateOfWorld.PodExistsInVolume(volumeToMount.PodName, volumeToMount.VolumeName, volumeToMount.DesiredPersistentVolumeSize, volumeToMount.SELinuxLabel)
volumeToMount.DevicePath = devicePath volumeToMount.DevicePath = devicePath
if cache.IsSELinuxMountMismatchError(err) { if cache.IsSELinuxMountMismatchError(err) {
// The volume is mounted, but with an unexpected SELinux context. // The volume is mounted, but with an unexpected SELinux context.

View File

@ -37,7 +37,7 @@ type NodeExpander struct {
// computed via precheck // computed via precheck
pvcStatusCap resource.Quantity pvcStatusCap resource.Quantity
pvCap resource.Quantity pvCap resource.Quantity
resizeStatus *v1.PersistentVolumeClaimResizeStatus resizeStatus v1.ClaimResourceStatus
// pvcAlreadyUpdated if true indicates that although we are calling NodeExpandVolume on the kubelet // pvcAlreadyUpdated if true indicates that although we are calling NodeExpandVolume on the kubelet
// PVC has already been updated - possibly because expansion already succeeded on different node. // PVC has already been updated - possibly because expansion already succeeded on different node.
@ -68,29 +68,37 @@ type testResponseData struct {
} }
// runPreCheck performs some sanity checks before expansion can be performed on the PVC. // runPreCheck performs some sanity checks before expansion can be performed on the PVC.
// This function returns true only if node expansion is allowed to proceed otherwise
// it returns false.
func (ne *NodeExpander) runPreCheck() bool { func (ne *NodeExpander) runPreCheck() bool {
ne.pvcStatusCap = ne.pvc.Status.Capacity[v1.ResourceStorage] ne.pvcStatusCap = ne.pvc.Status.Capacity[v1.ResourceStorage]
ne.pvCap = ne.pv.Spec.Capacity[v1.ResourceStorage] ne.pvCap = ne.pv.Spec.Capacity[v1.ResourceStorage]
ne.resizeStatus = ne.pvc.Status.ResizeStatus allocatedResourceStatus := ne.pvc.Status.AllocatedResourceStatuses
if currentStatus, ok := allocatedResourceStatus[v1.ResourceStorage]; ok {
ne.resizeStatus = currentStatus
}
// PVC is already expanded but we are still trying to expand the volume because // PVC is already expanded but we are still trying to expand the volume because
// last recorded size in ASOW is older. This can happen for RWX volume types. // last recorded size in ASOW is older. This can happen for RWX volume types.
if ne.pvcStatusCap.Cmp(ne.pluginResizeOpts.NewSize) >= 0 && (ne.resizeStatus == nil || *ne.resizeStatus == v1.PersistentVolumeClaimNoExpansionInProgress) { if ne.pvcStatusCap.Cmp(ne.pluginResizeOpts.NewSize) >= 0 && ne.resizeStatus == "" {
ne.pvcAlreadyUpdated = true ne.pvcAlreadyUpdated = true
}
// if resizestatus is nil or NodeExpansionInProgress or NodeExpansionPending then we
// should allow volume expansion on the node to proceed. We are making an exception for
// resizeStatus being nil because it will support use cases where
// resizeStatus may not be set (old control-plane expansion controller etc).
if ne.resizeStatus == nil ||
ne.pvcAlreadyUpdated ||
*ne.resizeStatus == v1.PersistentVolumeClaimNodeExpansionPending ||
*ne.resizeStatus == v1.PersistentVolumeClaimNodeExpansionInProgress {
return true return true
} }
// recovery features will only work for newer version of resize controller
if ne.resizeStatus == "" {
return false
}
resizeStatusVal := ne.resizeStatus
// if resizestatus is nil or NodeExpansionInProgress or NodeExpansionPending then we
// should allow volume expansion on the node to proceed.
if resizeStatusVal == v1.PersistentVolumeClaimNodeResizePending ||
resizeStatusVal == v1.PersistentVolumeClaimNodeResizeInProgress {
return true
}
return false return false
} }

View File

@ -17,6 +17,8 @@ limitations under the License.
package operationexecutor package operationexecutor
import ( import (
"testing"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
@ -24,10 +26,12 @@ import (
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
volumetesting "k8s.io/kubernetes/pkg/volume/testing" volumetesting "k8s.io/kubernetes/pkg/volume/testing"
"testing"
) )
func TestNodeExpander(t *testing.T) { func TestNodeExpander(t *testing.T) {
nodeResizeFailed := v1.PersistentVolumeClaimNodeResizeFailed
nodeResizePending := v1.PersistentVolumeClaimNodeResizePending
var tests = []struct { var tests = []struct {
name string name string
pvc *v1.PersistentVolumeClaim pvc *v1.PersistentVolumeClaim
@ -39,7 +43,7 @@ func TestNodeExpander(t *testing.T) {
actualSize *resource.Quantity actualSize *resource.Quantity
// expectations of test // expectations of test
expectedResizeStatus v1.PersistentVolumeClaimResizeStatus expectedResizeStatus v1.ClaimResourceStatus
expectedStatusSize resource.Quantity expectedStatusSize resource.Quantity
expectResizeCall bool expectResizeCall bool
assumeResizeOpAsFinished bool assumeResizeOpAsFinished bool
@ -47,39 +51,39 @@ func TestNodeExpander(t *testing.T) {
}{ }{
{ {
name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_failed", name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_failed",
pvc: getTestPVC("test-vol0", "2G", "1G", "", v1.PersistentVolumeClaimNodeExpansionFailed), pvc: getTestPVC("test-vol0", "2G", "1G", "", &nodeResizeFailed),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionFailed, expectedResizeStatus: nodeResizeFailed,
expectResizeCall: false, expectResizeCall: false,
assumeResizeOpAsFinished: true, assumeResizeOpAsFinished: true,
expectedStatusSize: resource.MustParse("1G"), expectedStatusSize: resource.MustParse("1G"),
}, },
{ {
name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending", name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending",
pvc: getTestPVC("test-vol0", "2G", "1G", "2G", v1.PersistentVolumeClaimNodeExpansionPending), pvc: getTestPVC("test-vol0", "2G", "1G", "2G", &nodeResizePending),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
expectedResizeStatus: v1.PersistentVolumeClaimNoExpansionInProgress, expectedResizeStatus: "",
expectResizeCall: true, expectResizeCall: true,
assumeResizeOpAsFinished: true, assumeResizeOpAsFinished: true,
expectedStatusSize: resource.MustParse("2G"), expectedStatusSize: resource.MustParse("2G"),
}, },
{ {
name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending, reize_op=failing", name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending, reize_op=failing",
pvc: getTestPVC(volumetesting.AlwaysFailNodeExpansion, "2G", "1G", "2G", v1.PersistentVolumeClaimNodeExpansionPending), pvc: getTestPVC(volumetesting.AlwaysFailNodeExpansion, "2G", "1G", "2G", &nodeResizePending),
pv: getTestPV(volumetesting.AlwaysFailNodeExpansion, "2G"), pv: getTestPV(volumetesting.AlwaysFailNodeExpansion, "2G"),
expectError: true, expectError: true,
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionFailed, expectedResizeStatus: nodeResizeFailed,
expectResizeCall: true, expectResizeCall: true,
assumeResizeOpAsFinished: true, assumeResizeOpAsFinished: true,
expectedStatusSize: resource.MustParse("1G"), expectedStatusSize: resource.MustParse("1G"),
}, },
{ {
name: "pv.spec.cap = pvc.status.cap, resizeStatus='', desiredSize > actualSize", name: "pv.spec.cap = pvc.status.cap, resizeStatus='', desiredSize > actualSize",
pvc: getTestPVC("test-vol0", "2G", "2G", "2G", v1.PersistentVolumeClaimNoExpansionInProgress), pvc: getTestPVC("test-vol0", "2G", "2G", "2G", nil),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
expectedResizeStatus: v1.PersistentVolumeClaimNoExpansionInProgress, expectedResizeStatus: "",
expectResizeCall: true, expectResizeCall: true,
assumeResizeOpAsFinished: true, assumeResizeOpAsFinished: true,
expectedStatusSize: resource.MustParse("2G"), expectedStatusSize: resource.MustParse("2G"),
@ -143,8 +147,11 @@ func TestNodeExpander(t *testing.T) {
if test.assumeResizeOpAsFinished != expansionResponse.assumeResizeFinished { if test.assumeResizeOpAsFinished != expansionResponse.assumeResizeFinished {
t.Errorf("For test %s, expected assumeResizeOpAsFinished %t, got %t", test.name, test.assumeResizeOpAsFinished, expansionResponse.assumeResizeFinished) t.Errorf("For test %s, expected assumeResizeOpAsFinished %t, got %t", test.name, test.assumeResizeOpAsFinished, expansionResponse.assumeResizeFinished)
} }
if test.expectedResizeStatus != *pvc.Status.ResizeStatus { allocatedResourceStatus := pvc.Status.AllocatedResourceStatuses
t.Errorf("For test %s, expected resizeStatus %v, got %v", test.name, test.expectedResizeStatus, *pvc.Status.ResizeStatus) resizeStatus := allocatedResourceStatus[v1.ResourceStorage]
if test.expectedResizeStatus != resizeStatus {
t.Errorf("For test %s, expected resizeStatus %v, got %v", test.name, test.expectedResizeStatus, resizeStatus)
} }
if pvcStatusCap.Cmp(test.expectedStatusSize) != 0 { if pvcStatusCap.Cmp(test.expectedStatusSize) != 0 {
t.Errorf("For test %s, expected status size %s, got %s", test.name, test.expectedStatusSize.String(), pvcStatusCap.String()) t.Errorf("For test %s, expected status size %s, got %s", test.name, test.expectedStatusSize.String(), pvcStatusCap.String())

View File

@ -440,9 +440,9 @@ type VolumeToMount struct {
// time at which volume was requested to be mounted // time at which volume was requested to be mounted
MountRequestTime time.Time MountRequestTime time.Time
// PersistentVolumeSize stores desired size of the volume. // DesiredPersistentVolumeSize stores desired size of the volume.
// usually this is the size if pv.Spec.Capacity // usually this is the size if pv.Spec.Capacity
PersistentVolumeSize resource.Quantity DesiredPersistentVolumeSize resource.Quantity
// SELinux label that should be used to mount. // SELinux label that should be used to mount.
SELinuxLabel string SELinuxLabel string

View File

@ -1781,12 +1781,14 @@ func (og *operationGenerator) expandAndRecoverFunction(resizeOpts inTreeResizeOp
resizeCalled: false, resizeCalled: false,
} }
// by default we are expanding to full-fill size requested in pvc.Spec.Resources // by default we are expanding to fulfill size requested in pvc.Spec.Resources
newSize := pvcSpecSize newSize := pvcSpecSize
resizeStatus := v1.PersistentVolumeClaimNoExpansionInProgress
if pvc.Status.ResizeStatus != nil { var resizeStatus v1.ClaimResourceStatus
resizeStatus = *pvc.Status.ResizeStatus if status, ok := pvc.Status.AllocatedResourceStatuses[v1.ResourceStorage]; ok {
resizeStatus = status
} }
var allocatedSize *resource.Quantity var allocatedSize *resource.Quantity
t, ok := pvc.Status.AllocatedResources[v1.ResourceStorage] t, ok := pvc.Status.AllocatedResources[v1.ResourceStorage]
if ok { if ok {
@ -1798,10 +1800,10 @@ func (og *operationGenerator) expandAndRecoverFunction(resizeOpts inTreeResizeOp
// pv is not of requested size yet and hence will require expanding // pv is not of requested size yet and hence will require expanding
switch resizeStatus { switch resizeStatus {
case v1.PersistentVolumeClaimControllerExpansionInProgress: case v1.PersistentVolumeClaimControllerResizeInProgress,
case v1.PersistentVolumeClaimNodeExpansionPending: v1.PersistentVolumeClaimNodeResizePending,
case v1.PersistentVolumeClaimNodeExpansionInProgress: v1.PersistentVolumeClaimNodeResizeInProgress,
case v1.PersistentVolumeClaimNodeExpansionFailed: v1.PersistentVolumeClaimNodeResizeFailed:
if allocatedSize != nil { if allocatedSize != nil {
newSize = *allocatedSize newSize = *allocatedSize
} }
@ -1820,30 +1822,29 @@ func (og *operationGenerator) expandAndRecoverFunction(resizeOpts inTreeResizeOp
// safe to do so. // safe to do so.
// 4. While expansion was still pending on the node, user reduced the pvc size. // 4. While expansion was still pending on the node, user reduced the pvc size.
switch resizeStatus { switch resizeStatus {
case v1.PersistentVolumeClaimNodeExpansionInProgress: case v1.PersistentVolumeClaimNodeResizeInProgress,
case v1.PersistentVolumeClaimNodeExpansionPending: v1.PersistentVolumeClaimNodeResizePending:
// we don't need to do any work. We could be here because of a spurious update event. // we don't need to do any work. We could be here because of a spurious update event.
// This is case #1 // This is case #1
return resizeResponse return resizeResponse
case v1.PersistentVolumeClaimNodeExpansionFailed: case v1.PersistentVolumeClaimNodeResizeFailed:
// This is case#3 // This is case#3
pvc, err = og.markForPendingNodeExpansion(pvc, pv) pvc, err = og.markForPendingNodeExpansion(pvc, pv)
resizeResponse.pvc = pvc resizeResponse.pvc = pvc
resizeResponse.err = err resizeResponse.err = err
return resizeResponse return resizeResponse
case v1.PersistentVolumeClaimControllerExpansionInProgress: case v1.PersistentVolumeClaimControllerResizeInProgress,
case v1.PersistentVolumeClaimControllerExpansionFailed: v1.PersistentVolumeClaimControllerResizeFailed:
case v1.PersistentVolumeClaimNoExpansionInProgress:
// This is case#2 or it could also be case#4 when user manually shrunk the PVC // This is case#2 or it could also be case#4 when user manually shrunk the PVC
// after expanding it. // after expanding it.
if allocatedSize != nil { if allocatedSize != nil {
newSize = *allocatedSize newSize = *allocatedSize
} }
default: default:
// It is impossible for ResizeStatus to be nil and allocatedSize to be not nil but somehow // It is impossible for ResizeStatus to be "" and allocatedSize to be not nil but somehow
// if we do end up in this state, it is safest to resume expansion to last recorded size in // if we do end up in this state, it is safest to resume expansion to last recorded size in
// allocatedSize variable. // allocatedSize variable.
if pvc.Status.ResizeStatus == nil && allocatedSize != nil { if resizeStatus == "" && allocatedSize != nil {
newSize = *allocatedSize newSize = *allocatedSize
} else { } else {
newSize = pvcSpecSize newSize = pvcSpecSize
@ -1938,7 +1939,7 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc(
var eventErr, detailedErr error var eventErr, detailedErr error
migrated := false migrated := false
if currentSize.IsZero() || volumeToMount.PersistentVolumeSize.IsZero() { if currentSize.IsZero() || volumeToMount.DesiredPersistentVolumeSize.IsZero() {
err := fmt.Errorf("current or new size of the volume is not set") err := fmt.Errorf("current or new size of the volume is not set")
eventErr, detailedErr = volumeToMount.GenerateError("NodeExpandvolume.expansion failed", err) eventErr, detailedErr = volumeToMount.GenerateError("NodeExpandvolume.expansion failed", err)
return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) return volumetypes.NewOperationContext(eventErr, detailedErr, migrated)
@ -1948,7 +1949,7 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc(
VolumeSpec: volumeToMount.VolumeSpec, VolumeSpec: volumeToMount.VolumeSpec,
DevicePath: volumeToMount.DevicePath, DevicePath: volumeToMount.DevicePath,
OldSize: currentSize, OldSize: currentSize,
NewSize: volumeToMount.PersistentVolumeSize, NewSize: volumeToMount.DesiredPersistentVolumeSize,
} }
fsVolume, err := util.CheckVolumeModeFilesystem(volumeToMount.VolumeSpec) fsVolume, err := util.CheckVolumeModeFilesystem(volumeToMount.VolumeSpec)
if err != nil { if err != nil {
@ -2168,7 +2169,7 @@ func (og *operationGenerator) nodeExpandVolume(
} }
func (og *operationGenerator) checkForRecoveryFromExpansion(pvc *v1.PersistentVolumeClaim, volumeToMount VolumeToMount) bool { func (og *operationGenerator) checkForRecoveryFromExpansion(pvc *v1.PersistentVolumeClaim, volumeToMount VolumeToMount) bool {
resizeStatus := pvc.Status.ResizeStatus resizeStatus := pvc.Status.AllocatedResourceStatuses[v1.ResourceStorage]
allocatedResource := pvc.Status.AllocatedResources allocatedResource := pvc.Status.AllocatedResources
featureGateStatus := utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) featureGateStatus := utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure)
@ -2179,7 +2180,7 @@ func (og *operationGenerator) checkForRecoveryFromExpansion(pvc *v1.PersistentVo
// Even though RecoverVolumeExpansionFailure feature gate is enabled, it appears that we are running with older version // Even though RecoverVolumeExpansionFailure feature gate is enabled, it appears that we are running with older version
// of resize controller, which will not populate allocatedResource and resizeStatus. This can happen because of version skew // of resize controller, which will not populate allocatedResource and resizeStatus. This can happen because of version skew
// and hence we are going to keep expanding using older logic. // and hence we are going to keep expanding using older logic.
if resizeStatus == nil && allocatedResource == nil { if resizeStatus == "" && allocatedResource == nil {
_, detailedMsg := volumeToMount.GenerateMsg("MountVolume.NodeExpandVolume running with", "older external resize controller") _, detailedMsg := volumeToMount.GenerateMsg("MountVolume.NodeExpandVolume running with", "older external resize controller")
klog.Warningf(detailedMsg) klog.Warningf(detailedMsg)
return false return false

View File

@ -93,6 +93,8 @@ func TestOperationGenerator_GenerateUnmapVolumeFunc_PluginName(t *testing.T) {
} }
func TestOperationGenerator_GenerateExpandAndRecoverVolumeFunc(t *testing.T) { func TestOperationGenerator_GenerateExpandAndRecoverVolumeFunc(t *testing.T) {
nodeResizePending := v1.PersistentVolumeClaimNodeResizePending
nodeResizeFailed := v1.PersistentVolumeClaimNodeResizeFailed
var tests = []struct { var tests = []struct {
name string name string
pvc *v1.PersistentVolumeClaim pvc *v1.PersistentVolumeClaim
@ -100,53 +102,53 @@ func TestOperationGenerator_GenerateExpandAndRecoverVolumeFunc(t *testing.T) {
recoverFeatureGate bool recoverFeatureGate bool
disableNodeExpansion bool disableNodeExpansion bool
// expectations of test // expectations of test
expectedResizeStatus v1.PersistentVolumeClaimResizeStatus expectedResizeStatus v1.ClaimResourceStatus
expectedAllocatedSize resource.Quantity expectedAllocatedSize resource.Quantity
expectResizeCall bool expectResizeCall bool
}{ }{
{ {
name: "pvc.spec.size > pv.spec.size, recover_expansion=on", name: "pvc.spec.size > pv.spec.size, recover_expansion=on",
pvc: getTestPVC("test-vol0", "2G", "1G", "", v1.PersistentVolumeClaimNoExpansionInProgress), pvc: getTestPVC("test-vol0", "2G", "1G", "", nil),
pv: getTestPV("test-vol0", "1G"), pv: getTestPV("test-vol0", "1G"),
recoverFeatureGate: true, recoverFeatureGate: true,
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionPending, expectedResizeStatus: v1.PersistentVolumeClaimNodeResizePending,
expectedAllocatedSize: resource.MustParse("2G"), expectedAllocatedSize: resource.MustParse("2G"),
expectResizeCall: true, expectResizeCall: true,
}, },
{ {
name: "pvc.spec.size = pv.spec.size, recover_expansion=on", name: "pvc.spec.size = pv.spec.size, recover_expansion=on",
pvc: getTestPVC("test-vol0", "1G", "1G", "", v1.PersistentVolumeClaimNoExpansionInProgress), pvc: getTestPVC("test-vol0", "1G", "1G", "", nil),
pv: getTestPV("test-vol0", "1G"), pv: getTestPV("test-vol0", "1G"),
recoverFeatureGate: true, recoverFeatureGate: true,
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionPending, expectedResizeStatus: v1.PersistentVolumeClaimNodeResizePending,
expectedAllocatedSize: resource.MustParse("1G"), expectedAllocatedSize: resource.MustParse("1G"),
expectResizeCall: true, expectResizeCall: true,
}, },
{ {
name: "pvc.spec.size = pv.spec.size, recover_expansion=on", name: "pvc.spec.size = pv.spec.size, recover_expansion=on",
pvc: getTestPVC("test-vol0", "1G", "1G", "1G", v1.PersistentVolumeClaimNodeExpansionPending), pvc: getTestPVC("test-vol0", "1G", "1G", "1G", &nodeResizePending),
pv: getTestPV("test-vol0", "1G"), pv: getTestPV("test-vol0", "1G"),
recoverFeatureGate: true, recoverFeatureGate: true,
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionPending, expectedResizeStatus: v1.PersistentVolumeClaimNodeResizePending,
expectedAllocatedSize: resource.MustParse("1G"), expectedAllocatedSize: resource.MustParse("1G"),
expectResizeCall: false, expectResizeCall: false,
}, },
{ {
name: "pvc.spec.size > pv.spec.size, recover_expansion=on, disable_node_expansion=true", name: "pvc.spec.size > pv.spec.size, recover_expansion=on, disable_node_expansion=true",
pvc: getTestPVC("test-vol0", "2G", "1G", "", v1.PersistentVolumeClaimNoExpansionInProgress), pvc: getTestPVC("test-vol0", "2G", "1G", "", nil),
pv: getTestPV("test-vol0", "1G"), pv: getTestPV("test-vol0", "1G"),
disableNodeExpansion: true, disableNodeExpansion: true,
recoverFeatureGate: true, recoverFeatureGate: true,
expectedResizeStatus: v1.PersistentVolumeClaimNoExpansionInProgress, expectedResizeStatus: "",
expectedAllocatedSize: resource.MustParse("2G"), expectedAllocatedSize: resource.MustParse("2G"),
expectResizeCall: true, expectResizeCall: true,
}, },
{ {
name: "pv.spec.size >= pvc.spec.size, recover_expansion=on, resize_status=node_expansion_failed", name: "pv.spec.size >= pvc.spec.size, recover_expansion=on, resize_status=node_expansion_failed",
pvc: getTestPVC("test-vol0", "2G", "1G", "2G", v1.PersistentVolumeClaimNodeExpansionFailed), pvc: getTestPVC("test-vol0", "2G", "1G", "2G", &nodeResizeFailed),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
recoverFeatureGate: true, recoverFeatureGate: true,
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionPending, expectedResizeStatus: v1.PersistentVolumeClaimNodeResizePending,
expectedAllocatedSize: resource.MustParse("2G"), expectedAllocatedSize: resource.MustParse("2G"),
expectResizeCall: false, expectResizeCall: false,
}, },
@ -173,7 +175,8 @@ func TestOperationGenerator_GenerateExpandAndRecoverVolumeFunc(t *testing.T) {
t.Fatalf("GenerateExpandAndRecoverVolumeFunc failed: %v", expansionResponse.err) t.Fatalf("GenerateExpandAndRecoverVolumeFunc failed: %v", expansionResponse.err)
} }
updatedPVC := expansionResponse.pvc updatedPVC := expansionResponse.pvc
assert.Equal(t, *updatedPVC.Status.ResizeStatus, test.expectedResizeStatus) actualResizeStatus := updatedPVC.Status.AllocatedResourceStatuses[v1.ResourceStorage]
assert.Equal(t, actualResizeStatus, test.expectedResizeStatus)
actualAllocatedSize := updatedPVC.Status.AllocatedResources.Storage() actualAllocatedSize := updatedPVC.Status.AllocatedResources.Storage()
if test.expectedAllocatedSize.Cmp(*actualAllocatedSize) != 0 { if test.expectedAllocatedSize.Cmp(*actualAllocatedSize) != 0 {
t.Fatalf("GenerateExpandAndRecoverVolumeFunc failed: expected allocated size %s, got %s", test.expectedAllocatedSize.String(), actualAllocatedSize.String()) t.Fatalf("GenerateExpandAndRecoverVolumeFunc failed: expected allocated size %s, got %s", test.expectedAllocatedSize.String(), actualAllocatedSize.String())
@ -191,6 +194,8 @@ func TestOperationGenerator_nodeExpandVolume(t *testing.T) {
return &x return &x
} }
nodeResizeFailed := v1.PersistentVolumeClaimNodeResizeFailed
nodeResizePending := v1.PersistentVolumeClaimNodeResizePending
var tests = []struct { var tests = []struct {
name string name string
pvc *v1.PersistentVolumeClaim pvc *v1.PersistentVolumeClaim
@ -202,65 +207,65 @@ func TestOperationGenerator_nodeExpandVolume(t *testing.T) {
actualSize *resource.Quantity actualSize *resource.Quantity
// expectations of test // expectations of test
expectedResizeStatus v1.PersistentVolumeClaimResizeStatus expectedResizeStatus v1.ClaimResourceStatus
expectedStatusSize resource.Quantity expectedStatusSize resource.Quantity
resizeCallCount int resizeCallCount int
expectError bool expectError bool
}{ }{
{ {
name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_failed", name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_failed",
pvc: getTestPVC("test-vol0", "2G", "1G", "", v1.PersistentVolumeClaimNodeExpansionFailed), pvc: getTestPVC("test-vol0", "2G", "1G", "", &nodeResizeFailed),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionFailed, expectedResizeStatus: v1.PersistentVolumeClaimNodeResizeFailed,
resizeCallCount: 0, resizeCallCount: 0,
expectedStatusSize: resource.MustParse("1G"), expectedStatusSize: resource.MustParse("1G"),
}, },
{ {
name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending", name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending",
pvc: getTestPVC("test-vol0", "2G", "1G", "2G", v1.PersistentVolumeClaimNodeExpansionPending), pvc: getTestPVC("test-vol0", "2G", "1G", "2G", &nodeResizePending),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
expectedResizeStatus: v1.PersistentVolumeClaimNoExpansionInProgress, expectedResizeStatus: "",
resizeCallCount: 1, resizeCallCount: 1,
expectedStatusSize: resource.MustParse("2G"), expectedStatusSize: resource.MustParse("2G"),
}, },
{ {
name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending, reize_op=failing", name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending, reize_op=failing",
pvc: getTestPVC(volumetesting.AlwaysFailNodeExpansion, "2G", "1G", "2G", v1.PersistentVolumeClaimNodeExpansionPending), pvc: getTestPVC(volumetesting.AlwaysFailNodeExpansion, "2G", "1G", "2G", &nodeResizePending),
pv: getTestPV(volumetesting.AlwaysFailNodeExpansion, "2G"), pv: getTestPV(volumetesting.AlwaysFailNodeExpansion, "2G"),
expectError: true, expectError: true,
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionFailed, expectedResizeStatus: v1.PersistentVolumeClaimNodeResizeFailed,
resizeCallCount: 1, resizeCallCount: 1,
expectedStatusSize: resource.MustParse("1G"), expectedStatusSize: resource.MustParse("1G"),
}, },
{ {
name: "pv.spec.cap = pvc.status.cap, resizeStatus='', desiredSize = actualSize", name: "pv.spec.cap = pvc.status.cap, resizeStatus='', desiredSize = actualSize",
pvc: getTestPVC("test-vol0", "2G", "2G", "2G", v1.PersistentVolumeClaimNoExpansionInProgress), pvc: getTestPVC("test-vol0", "2G", "2G", "2G", nil),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
expectedResizeStatus: v1.PersistentVolumeClaimNoExpansionInProgress, expectedResizeStatus: "",
resizeCallCount: 0, resizeCallCount: 0,
expectedStatusSize: resource.MustParse("2G"), expectedStatusSize: resource.MustParse("2G"),
}, },
{ {
name: "pv.spec.cap = pvc.status.cap, resizeStatus='', desiredSize > actualSize", name: "pv.spec.cap = pvc.status.cap, resizeStatus='', desiredSize > actualSize",
pvc: getTestPVC("test-vol0", "2G", "2G", "2G", v1.PersistentVolumeClaimNoExpansionInProgress), pvc: getTestPVC("test-vol0", "2G", "2G", "2G", nil),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
desiredSize: getSizeFunc("2G"), desiredSize: getSizeFunc("2G"),
actualSize: getSizeFunc("1G"), actualSize: getSizeFunc("1G"),
expectedResizeStatus: v1.PersistentVolumeClaimNoExpansionInProgress, expectedResizeStatus: "",
resizeCallCount: 1, resizeCallCount: 1,
expectedStatusSize: resource.MustParse("2G"), expectedStatusSize: resource.MustParse("2G"),
}, },
{ {
name: "pv.spec.cap = pvc.status.cap, resizeStatus=node-expansion-failed, desiredSize > actualSize", name: "pv.spec.cap = pvc.status.cap, resizeStatus=node-expansion-failed, desiredSize > actualSize",
pvc: getTestPVC("test-vol0", "2G", "2G", "2G", v1.PersistentVolumeClaimNodeExpansionFailed), pvc: getTestPVC("test-vol0", "2G", "2G", "2G", &nodeResizeFailed),
pv: getTestPV("test-vol0", "2G"), pv: getTestPV("test-vol0", "2G"),
desiredSize: getSizeFunc("2G"), desiredSize: getSizeFunc("2G"),
actualSize: getSizeFunc("1G"), actualSize: getSizeFunc("1G"),
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionFailed, expectedResizeStatus: v1.PersistentVolumeClaimNodeResizeFailed,
resizeCallCount: 0, resizeCallCount: 0,
expectedStatusSize: resource.MustParse("2G"), expectedStatusSize: resource.MustParse("2G"),
}, },
@ -336,7 +341,7 @@ func getTestPod(podName, pvcName string) *v1.Pod {
} }
} }
func getTestPVC(volumeName string, specSize, statusSize, allocatedSize string, resizeStatus v1.PersistentVolumeClaimResizeStatus) *v1.PersistentVolumeClaim { func getTestPVC(volumeName string, specSize, statusSize, allocatedSize string, resizeStatus *v1.ClaimResourceStatus) *v1.PersistentVolumeClaim {
pvc := &v1.PersistentVolumeClaim{ pvc := &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "claim01", Name: "claim01",
@ -358,7 +363,11 @@ func getTestPVC(volumeName string, specSize, statusSize, allocatedSize string, r
if len(allocatedSize) > 0 { if len(allocatedSize) > 0 {
pvc.Status.AllocatedResources = v1.ResourceList{v1.ResourceStorage: resource.MustParse(allocatedSize)} pvc.Status.AllocatedResources = v1.ResourceList{v1.ResourceStorage: resource.MustParse(allocatedSize)}
} }
pvc.Status.ResizeStatus = &resizeStatus if resizeStatus != nil {
pvc.Status.AllocatedResourceStatuses = map[v1.ResourceName]v1.ClaimResourceStatus{
v1.ResourceStorage: *resizeStatus,
}
}
return pvc return pvc
} }

View File

@ -152,12 +152,11 @@ func MarkControllerReisizeInProgress(pvc *v1.PersistentVolumeClaim, resizerName
Status: v1.ConditionTrue, Status: v1.ConditionTrue,
LastTransitionTime: metav1.Now(), LastTransitionTime: metav1.Now(),
} }
controllerExpansionInProgress := v1.PersistentVolumeClaimControllerExpansionInProgress
conditions := []v1.PersistentVolumeClaimCondition{progressCondition} conditions := []v1.PersistentVolumeClaimCondition{progressCondition}
newPVC := pvc.DeepCopy() newPVC := pvc.DeepCopy()
newPVC = MergeResizeConditionOnPVC(newPVC, conditions) newPVC = MergeResizeConditionOnPVC(newPVC, conditions)
newPVC.Status.ResizeStatus = &controllerExpansionInProgress newPVC = mergeStorageResourceStatus(newPVC, v1.PersistentVolumeClaimControllerResizeInProgress)
newPVC.Status.AllocatedResources = v1.ResourceList{v1.ResourceStorage: newSize} newPVC = mergeStorageAllocatedResources(newPVC, newSize)
newPVC = setResizer(newPVC, resizerName) newPVC = setResizer(newPVC, resizerName)
return PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient) return PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient)
} }
@ -192,10 +191,11 @@ func MarkForFSResize(
} }
conditions := []v1.PersistentVolumeClaimCondition{pvcCondition} conditions := []v1.PersistentVolumeClaimCondition{pvcCondition}
newPVC := pvc.DeepCopy() newPVC := pvc.DeepCopy()
if utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) { if utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) {
expansionPendingOnNode := v1.PersistentVolumeClaimNodeExpansionPending newPVC = mergeStorageResourceStatus(newPVC, v1.PersistentVolumeClaimNodeResizePending)
newPVC.Status.ResizeStatus = &expansionPendingOnNode
} }
newPVC = MergeResizeConditionOnPVC(newPVC, conditions) newPVC = MergeResizeConditionOnPVC(newPVC, conditions)
updatedPVC, err := PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient) updatedPVC, err := PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient)
return updatedPVC, err return updatedPVC, err
@ -220,8 +220,13 @@ func MarkFSResizeFinished(
// if RecoverVolumeExpansionFailure is enabled, we need to reset ResizeStatus back to nil // if RecoverVolumeExpansionFailure is enabled, we need to reset ResizeStatus back to nil
if utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) { if utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) {
expansionFinished := v1.PersistentVolumeClaimNoExpansionInProgress allocatedResourceStatusMap := newPVC.Status.AllocatedResourceStatuses
newPVC.Status.ResizeStatus = &expansionFinished delete(allocatedResourceStatusMap, v1.ResourceStorage)
if len(allocatedResourceStatusMap) == 0 {
newPVC.Status.AllocatedResourceStatuses = nil
} else {
newPVC.Status.AllocatedResourceStatuses = allocatedResourceStatusMap
}
} }
newPVC = MergeResizeConditionOnPVC(newPVC, []v1.PersistentVolumeClaimCondition{}) newPVC = MergeResizeConditionOnPVC(newPVC, []v1.PersistentVolumeClaimCondition{})
@ -232,9 +237,9 @@ func MarkFSResizeFinished(
// MarkNodeExpansionFailed marks a PVC for node expansion as failed. Kubelet should not retry expansion // MarkNodeExpansionFailed marks a PVC for node expansion as failed. Kubelet should not retry expansion
// of volumes which are in failed state. // of volumes which are in failed state.
func MarkNodeExpansionFailed(pvc *v1.PersistentVolumeClaim, kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) { func MarkNodeExpansionFailed(pvc *v1.PersistentVolumeClaim, kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) {
expansionFailedOnNode := v1.PersistentVolumeClaimNodeExpansionFailed
newPVC := pvc.DeepCopy() newPVC := pvc.DeepCopy()
newPVC.Status.ResizeStatus = &expansionFailedOnNode newPVC = mergeStorageResourceStatus(newPVC, v1.PersistentVolumeClaimNodeResizeFailed)
patchBytes, err := createPVCPatch(pvc, newPVC, false /* addResourceVersionCheck */) patchBytes, err := createPVCPatch(pvc, newPVC, false /* addResourceVersionCheck */)
if err != nil { if err != nil {
return pvc, fmt.Errorf("patchPVCStatus failed to patch PVC %q: %v", pvc.Name, err) return pvc, fmt.Errorf("patchPVCStatus failed to patch PVC %q: %v", pvc.Name, err)
@ -250,9 +255,8 @@ func MarkNodeExpansionFailed(pvc *v1.PersistentVolumeClaim, kubeClient clientset
// MarkNodeExpansionInProgress marks pvc expansion in progress on node // MarkNodeExpansionInProgress marks pvc expansion in progress on node
func MarkNodeExpansionInProgress(pvc *v1.PersistentVolumeClaim, kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) { func MarkNodeExpansionInProgress(pvc *v1.PersistentVolumeClaim, kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) {
nodeExpansionInProgress := v1.PersistentVolumeClaimNodeExpansionInProgress
newPVC := pvc.DeepCopy() newPVC := pvc.DeepCopy()
newPVC.Status.ResizeStatus = &nodeExpansionInProgress newPVC = mergeStorageResourceStatus(newPVC, v1.PersistentVolumeClaimNodeResizeInProgress)
updatedPVC, err := PatchPVCStatus(pvc /* oldPVC */, newPVC, kubeClient) updatedPVC, err := PatchPVCStatus(pvc /* oldPVC */, newPVC, kubeClient)
return updatedPVC, err return updatedPVC, err
} }
@ -365,6 +369,32 @@ func MergeResizeConditionOnPVC(
return pvc return pvc
} }
func mergeStorageResourceStatus(pvc *v1.PersistentVolumeClaim, status v1.ClaimResourceStatus) *v1.PersistentVolumeClaim {
allocatedResourceStatusMap := pvc.Status.AllocatedResourceStatuses
if allocatedResourceStatusMap == nil {
pvc.Status.AllocatedResourceStatuses = map[v1.ResourceName]v1.ClaimResourceStatus{
v1.ResourceStorage: status,
}
return pvc
}
allocatedResourceStatusMap[v1.ResourceStorage] = status
pvc.Status.AllocatedResourceStatuses = allocatedResourceStatusMap
return pvc
}
func mergeStorageAllocatedResources(pvc *v1.PersistentVolumeClaim, size resource.Quantity) *v1.PersistentVolumeClaim {
allocatedResourcesMap := pvc.Status.AllocatedResources
if allocatedResourcesMap == nil {
pvc.Status.AllocatedResources = map[v1.ResourceName]resource.Quantity{
v1.ResourceStorage: size,
}
return pvc
}
allocatedResourcesMap[v1.ResourceStorage] = size
pvc.Status.AllocatedResources = allocatedResourcesMap
return pvc
}
// GenericResizeFS : call generic filesystem resizer for plugins that don't have any special filesystem resize requirements // GenericResizeFS : call generic filesystem resizer for plugins that don't have any special filesystem resize requirements
func GenericResizeFS(host volume.VolumeHost, pluginName, devicePath, deviceMountPath string) (bool, error) { func GenericResizeFS(host volume.VolumeHost, pluginName, devicePath, deviceMountPath string) (bool, error) {
resizer := mount.NewResizeFs(host.GetExec(pluginName)) resizer := mount.NewResizeFs(host.GetExec(pluginName))

View File

@ -360,8 +360,8 @@ func (p *Plugin) admitPVCStatus(nodeName string, a admission.Attributes) error {
newPVC.Status.Conditions = nil newPVC.Status.Conditions = nil
if p.expansionRecoveryEnabled { if p.expansionRecoveryEnabled {
oldPVC.Status.ResizeStatus = nil oldPVC.Status.AllocatedResourceStatuses = nil
newPVC.Status.ResizeStatus = nil newPVC.Status.AllocatedResourceStatuses = nil
oldPVC.Status.AllocatedResources = nil oldPVC.Status.AllocatedResources = nil
newPVC.Status.AllocatedResources = nil newPVC.Status.AllocatedResources = nil

View File

@ -1438,6 +1438,8 @@ func TestAdmitPVCStatus(t *testing.T) {
noExistingPods := corev1lister.NewPodLister(noExistingPodsIndex) noExistingPods := corev1lister.NewPodLister(noExistingPodsIndex)
mynode := &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}} mynode := &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}}
nodeExpansionFailed := api.PersistentVolumeClaimNodeResizeFailed
tests := []struct { tests := []struct {
name string name string
resource schema.GroupVersionResource resource schema.GroupVersionResource
@ -1452,11 +1454,11 @@ func TestAdmitPVCStatus(t *testing.T) {
name: "should not allow full pvc update from nodes", name: "should not allow full pvc update from nodes",
oldObj: makeTestPVC( oldObj: makeTestPVC(
api.PersistentVolumeClaimResizing, api.PersistentVolumeClaimResizing,
api.PersistentVolumeClaimNoExpansionInProgress, "10G", "10G", nil,
), ),
subresource: "", subresource: "",
newObj: makeTestPVC( newObj: makeTestPVC(
"", api.PersistentVolumeClaimNoExpansionInProgress, "10G", "", "10G", nil,
), ),
expectError: "is forbidden: may only update PVC status", expectError: "is forbidden: may only update PVC status",
}, },
@ -1464,13 +1466,13 @@ func TestAdmitPVCStatus(t *testing.T) {
name: "should allow capacity and condition updates, if expansion is enabled", name: "should allow capacity and condition updates, if expansion is enabled",
oldObj: makeTestPVC( oldObj: makeTestPVC(
api.PersistentVolumeClaimResizing, api.PersistentVolumeClaimResizing,
api.PersistentVolumeClaimNoExpansionInProgress, "10G", "10G", nil,
), ),
expansionFeatureEnabled: true, expansionFeatureEnabled: true,
subresource: "status", subresource: "status",
newObj: makeTestPVC( newObj: makeTestPVC(
api.PersistentVolumeClaimFileSystemResizePending, api.PersistentVolumeClaimFileSystemResizePending,
api.PersistentVolumeClaimNoExpansionInProgress, "10G", "10G", nil,
), ),
expectError: "", expectError: "",
}, },
@ -1478,13 +1480,13 @@ func TestAdmitPVCStatus(t *testing.T) {
name: "should not allow updates to allocatedResources with just expansion enabled", name: "should not allow updates to allocatedResources with just expansion enabled",
oldObj: makeTestPVC( oldObj: makeTestPVC(
api.PersistentVolumeClaimResizing, api.PersistentVolumeClaimResizing,
api.PersistentVolumeClaimNoExpansionInProgress, "10G", "10G", nil,
), ),
subresource: "status", subresource: "status",
expansionFeatureEnabled: true, expansionFeatureEnabled: true,
newObj: makeTestPVC( newObj: makeTestPVC(
api.PersistentVolumeClaimFileSystemResizePending, api.PersistentVolumeClaimFileSystemResizePending,
api.PersistentVolumeClaimNoExpansionInProgress, "15G", "15G", nil,
), ),
expectError: "is not allowed to update fields other than", expectError: "is not allowed to update fields other than",
}, },
@ -1492,14 +1494,14 @@ func TestAdmitPVCStatus(t *testing.T) {
name: "should allow updates to allocatedResources with expansion and recovery enabled", name: "should allow updates to allocatedResources with expansion and recovery enabled",
oldObj: makeTestPVC( oldObj: makeTestPVC(
api.PersistentVolumeClaimResizing, api.PersistentVolumeClaimResizing,
api.PersistentVolumeClaimNoExpansionInProgress, "10G", "10G", nil,
), ),
subresource: "status", subresource: "status",
expansionFeatureEnabled: true, expansionFeatureEnabled: true,
recoveryFeatureEnabled: true, recoveryFeatureEnabled: true,
newObj: makeTestPVC( newObj: makeTestPVC(
api.PersistentVolumeClaimFileSystemResizePending, api.PersistentVolumeClaimFileSystemResizePending,
api.PersistentVolumeClaimNoExpansionInProgress, "15G", "15G", nil,
), ),
expectError: "", expectError: "",
}, },
@ -1507,14 +1509,14 @@ func TestAdmitPVCStatus(t *testing.T) {
name: "should allow updates to resizeStatus with expansion and recovery enabled", name: "should allow updates to resizeStatus with expansion and recovery enabled",
oldObj: makeTestPVC( oldObj: makeTestPVC(
api.PersistentVolumeClaimResizing, api.PersistentVolumeClaimResizing,
api.PersistentVolumeClaimNoExpansionInProgress, "10G", "10G", nil,
), ),
subresource: "status", subresource: "status",
expansionFeatureEnabled: true, expansionFeatureEnabled: true,
recoveryFeatureEnabled: true, recoveryFeatureEnabled: true,
newObj: makeTestPVC( newObj: makeTestPVC(
api.PersistentVolumeClaimResizing, api.PersistentVolumeClaimResizing,
api.PersistentVolumeClaimNodeExpansionFailed, "10G", "10G", &nodeExpansionFailed,
), ),
expectError: "", expectError: "",
}, },
@ -1545,8 +1547,8 @@ func TestAdmitPVCStatus(t *testing.T) {
func makeTestPVC( func makeTestPVC(
condition api.PersistentVolumeClaimConditionType, condition api.PersistentVolumeClaimConditionType,
resizeStatus api.PersistentVolumeClaimResizeStatus, allocatedResources string,
allocatedResources string) *api.PersistentVolumeClaim { resizeStatus *api.ClaimResourceStatus) *api.PersistentVolumeClaim {
pvc := &api.PersistentVolumeClaim{ pvc := &api.PersistentVolumeClaim{
Spec: api.PersistentVolumeClaimSpec{ Spec: api.PersistentVolumeClaimSpec{
VolumeName: "volume1", VolumeName: "volume1",
@ -1560,13 +1562,19 @@ func makeTestPVC(
Capacity: api.ResourceList{ Capacity: api.ResourceList{
api.ResourceStorage: resource.MustParse(allocatedResources), api.ResourceStorage: resource.MustParse(allocatedResources),
}, },
Phase: api.ClaimBound, Phase: api.ClaimBound,
ResizeStatus: &resizeStatus,
AllocatedResources: api.ResourceList{ AllocatedResources: api.ResourceList{
api.ResourceStorage: resource.MustParse(allocatedResources), api.ResourceStorage: resource.MustParse(allocatedResources),
}, },
}, },
} }
if resizeStatus != nil {
claimStatusMap := map[api.ResourceName]api.ClaimResourceStatus{
api.ResourceStorage: *resizeStatus,
}
pvc.Status.AllocatedResourceStatuses = claimStatusMap
}
if len(condition) > 0 { if len(condition) > 0 {
pvc.Status.Conditions = []api.PersistentVolumeClaimCondition{ pvc.Status.Conditions = []api.PersistentVolumeClaimCondition{
{ {

View File

@ -558,23 +558,23 @@ const (
) )
// +enum // +enum
type PersistentVolumeClaimResizeStatus string type ClaimResourceStatus string
const ( const (
// When expansion is complete, the empty string is set by resize controller or kubelet.
PersistentVolumeClaimNoExpansionInProgress PersistentVolumeClaimResizeStatus = ""
// State set when resize controller starts expanding the volume in control-plane // State set when resize controller starts expanding the volume in control-plane
PersistentVolumeClaimControllerExpansionInProgress PersistentVolumeClaimResizeStatus = "ControllerExpansionInProgress" PersistentVolumeClaimControllerResizeInProgress ClaimResourceStatus = "ControllerResizeInProgress"
// State set when expansion has failed in resize controller with a terminal error. // State set when expansion has failed in resize controller with a terminal error.
// Transient errors such as timeout should not set this status and should leave ResizeStatus // Transient errors such as timeout should not set this status and should leave allocatedResourceStatus
// unmodified, so as resize controller can resume the volume expansion. // unmodified, so as resize controller can resume the volume expansion.
PersistentVolumeClaimControllerExpansionFailed PersistentVolumeClaimResizeStatus = "ControllerExpansionFailed" PersistentVolumeClaimControllerResizeFailed ClaimResourceStatus = "ControllerResizeFailed"
// State set when resize controller has finished expanding the volume but further expansion is needed on the node. // State set when resize controller has finished expanding the volume but further expansion is needed on the node.
PersistentVolumeClaimNodeExpansionPending PersistentVolumeClaimResizeStatus = "NodeExpansionPending" PersistentVolumeClaimNodeResizePending ClaimResourceStatus = "NodeResizePending"
// State set when kubelet starts expanding the volume. // State set when kubelet starts expanding the volume.
PersistentVolumeClaimNodeExpansionInProgress PersistentVolumeClaimResizeStatus = "NodeExpansionInProgress" PersistentVolumeClaimNodeResizeInProgress ClaimResourceStatus = "NodeResizeInProgress"
// State set when expansion has failed in kubelet with a terminal error. Transient errors don't set NodeExpansionFailed. // State set when expansion has failed in kubelet with a terminal error. Transient errors don't set NodeExpansionFailed.
PersistentVolumeClaimNodeExpansionFailed PersistentVolumeClaimResizeStatus = "NodeExpansionFailed" PersistentVolumeClaimNodeResizeFailed ClaimResourceStatus = "NodeResizeFailed"
) )
// PersistentVolumeClaimCondition contains details about state of pvc // PersistentVolumeClaimCondition contains details about state of pvc
@ -626,13 +626,23 @@ type PersistentVolumeClaimStatus struct {
// +featureGate=RecoverVolumeExpansionFailure // +featureGate=RecoverVolumeExpansionFailure
// +optional // +optional
AllocatedResources ResourceList `json:"allocatedResources,omitempty" protobuf:"bytes,5,rep,name=allocatedResources,casttype=ResourceList,castkey=ResourceName"` AllocatedResources ResourceList `json:"allocatedResources,omitempty" protobuf:"bytes,5,rep,name=allocatedResources,casttype=ResourceList,castkey=ResourceName"`
// resizeStatus stores status of resize operation.
// ResizeStatus is not set by default but when expansion is complete resizeStatus is set to empty // resizestatus is tombstoned since the field was replaced by allocatedResourceStatus
// string by resize controller or kubelet. // ResizeStatus *PersistentVolumeClaimResizeStatus `json:"resizeStatus,omitempty" protobuf:"bytes,6,opt,name=resizeStatus,casttype=PersistentVolumeClaimResizeStatus"`
// allocatedResourceStatuses stores status of resource being resized for the given PVC.
// If Expanding a PVC for more capacity - this field can be one of the following states:
// - pvc.status.allocatedResourceStatus['storage'] = "ControllerResizeInProgress"
// - pvc.status.allocatedResourceStatus['storage'] = "ControllerResizeFailed"
// - pvc.status.allocatedResourceStatus['storage'] = "NodeResizePending"
// - pvc.status.allocatedResourceStatus['storage'] = "NodeResizeInProgress"
// - pvc.status.allocatedResourceStatus['storage'] = "NodeResizeFailed"
// When this field is not set, it means that no resize operation is in progress for the given PVC.
// This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature. // This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.
// +featureGate=RecoverVolumeExpansionFailure // +featureGate=RecoverVolumeExpansionFailure
// +mapType=granular
// +optional // +optional
ResizeStatus *PersistentVolumeClaimResizeStatus `json:"resizeStatus,omitempty" protobuf:"bytes,6,opt,name=resizeStatus,casttype=PersistentVolumeClaimResizeStatus"` AllocatedResourceStatuses map[ResourceName]ClaimResourceStatus `json:"allocatedResourceStatuses,omitempty" protobuf:"bytes,7,rep,name=allocatedResourceStatuses"`
} }
// +enum // +enum

View File

@ -63,7 +63,7 @@ type recoveryTest struct {
pvcRequestSize string pvcRequestSize string
allocatedResource string allocatedResource string
simulatedCSIDriverError expansionStatus simulatedCSIDriverError expansionStatus
expectedResizeStatus v1.PersistentVolumeClaimResizeStatus expectedResizeStatus v1.ClaimResourceStatus
recoverySize resource.Quantity recoverySize resource.Quantity
} }
@ -403,14 +403,14 @@ var _ = utils.SIGDescribe("CSI Mock volume expansion", func() {
pvcRequestSize: "4Gi", pvcRequestSize: "4Gi",
allocatedResource: "4Gi", allocatedResource: "4Gi",
simulatedCSIDriverError: expansionSuccess, simulatedCSIDriverError: expansionSuccess,
expectedResizeStatus: v1.PersistentVolumeClaimNoExpansionInProgress, expectedResizeStatus: "",
}, },
{ {
name: "should allow recovery if controller expansion fails with final error", name: "should allow recovery if controller expansion fails with final error",
pvcRequestSize: "11Gi", // expansion to 11Gi will cause expansion to fail on controller pvcRequestSize: "11Gi", // expansion to 11Gi will cause expansion to fail on controller
allocatedResource: "11Gi", allocatedResource: "11Gi",
simulatedCSIDriverError: expansionFailedOnController, simulatedCSIDriverError: expansionFailedOnController,
expectedResizeStatus: v1.PersistentVolumeClaimControllerExpansionFailed, expectedResizeStatus: v1.PersistentVolumeClaimControllerResizeFailed,
recoverySize: resource.MustParse("4Gi"), recoverySize: resource.MustParse("4Gi"),
}, },
{ {
@ -418,7 +418,7 @@ var _ = utils.SIGDescribe("CSI Mock volume expansion", func() {
pvcRequestSize: "9Gi", // expansion to 9Gi will cause expansion to fail on node pvcRequestSize: "9Gi", // expansion to 9Gi will cause expansion to fail on node
allocatedResource: "9Gi", allocatedResource: "9Gi",
simulatedCSIDriverError: expansionFailedOnNode, simulatedCSIDriverError: expansionFailedOnNode,
expectedResizeStatus: v1.PersistentVolumeClaimNodeExpansionFailed, expectedResizeStatus: v1.PersistentVolumeClaimNodeResizeFailed,
recoverySize: resource.MustParse("5Gi"), recoverySize: resource.MustParse("5Gi"),
}, },
} }
@ -499,7 +499,7 @@ func validateRecoveryBehaviour(ctx context.Context, pvc *v1.PersistentVolumeClai
// if expansion succeeded on controller but failed on the node // if expansion succeeded on controller but failed on the node
if test.simulatedCSIDriverError == expansionFailedOnNode { if test.simulatedCSIDriverError == expansionFailedOnNode {
ginkgo.By("Wait for expansion to fail on node again") ginkgo.By("Wait for expansion to fail on node again")
err = waitForResizeStatus(pvc, m.cs, v1.PersistentVolumeClaimNodeExpansionFailed) err = waitForResizeStatus(pvc, m.cs, v1.PersistentVolumeClaimNodeResizeFailed)
framework.ExpectNoError(err, "While waiting for resize status to be set to expansion-failed-on-node") framework.ExpectNoError(err, "While waiting for resize status to be set to expansion-failed-on-node")
ginkgo.By("verify allocated resources after recovery") ginkgo.By("verify allocated resources after recovery")
@ -536,13 +536,13 @@ func validateExpansionSuccess(ctx context.Context, pvc *v1.PersistentVolumeClaim
framework.Failf("expected allocated Resources to be %s got %s", expectedAllocatedResource.String(), allocatedResource.String()) framework.Failf("expected allocated Resources to be %s got %s", expectedAllocatedResource.String(), allocatedResource.String())
} }
resizeStatus := pvc.Status.ResizeStatus resizeStatus := pvc.Status.AllocatedResourceStatuses[v1.ResourceStorage]
gomega.Expect(resizeStatus).NotTo(gomega.BeNil(), "resize status should not be nil") framework.ExpectEqual(resizeStatus, "", "resize status should be empty")
framework.ExpectEqual(*resizeStatus, v1.PersistentVolumeClaimNoExpansionInProgress, "resize status should be empty")
} }
func waitForResizeStatus(pvc *v1.PersistentVolumeClaim, c clientset.Interface, expectedStates ...v1.PersistentVolumeClaimResizeStatus) error { func waitForResizeStatus(pvc *v1.PersistentVolumeClaim, c clientset.Interface, expectedState v1.ClaimResourceStatus) error {
var actualResizeStatus *v1.PersistentVolumeClaimResizeStatus var actualResizeStatus *v1.ClaimResourceStatus
waitErr := wait.PollImmediate(resizePollInterval, csiResizeWaitPeriod, func() (bool, error) { waitErr := wait.PollImmediate(resizePollInterval, csiResizeWaitPeriod, func() (bool, error) {
var err error var err error
updatedPVC, err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{}) updatedPVC, err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{})
@ -551,18 +551,11 @@ func waitForResizeStatus(pvc *v1.PersistentVolumeClaim, c clientset.Interface, e
return false, fmt.Errorf("error fetching pvc %q for checking for resize status: %w", pvc.Name, err) return false, fmt.Errorf("error fetching pvc %q for checking for resize status: %w", pvc.Name, err)
} }
actualResizeStatus = updatedPVC.Status.ResizeStatus actualResizeStatus := updatedPVC.Status.AllocatedResourceStatuses[v1.ResourceStorage]
if actualResizeStatus != nil { return (actualResizeStatus == expectedState), nil
for _, s := range expectedStates {
if s == *actualResizeStatus {
return true, nil
}
}
}
return false, nil
}) })
if waitErr != nil { if waitErr != nil {
return fmt.Errorf("error while waiting for resize status to sync to %+v, actualStatus %s: %v", expectedStates, *actualResizeStatus, waitErr) return fmt.Errorf("error while waiting for resize status to sync to %v, actualStatus %s: %v", expectedState, *actualResizeStatus, waitErr)
} }
return nil return nil
} }