Merge pull request #58256 from mlmhl/pvc_storageclass_upgrade_path

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Admit upgrading storage class of pvc from beta annotation to spec field

**What this PR does / why we need it**:

`BetaStorageClassAnnotation` is marked as deprecated so we need to provide an upgrade path from PVC with storage class specified in beta annotation to storage class specified in attribute.

ref: #58147 

**Release note**:

```release-note
NONE
```

/sig storage
pull/6/head
Kubernetes Submit Queue 2018-02-05 19:02:26 -08:00 committed by GitHub
commit 69f8b1559f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 137 additions and 4 deletions

View File

@ -1783,6 +1783,16 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl
oldPvcClone.Spec.VolumeName = newPvcClone.Spec.VolumeName
}
if validateStorageClassUpgrade(oldPvcClone.Annotations, newPvcClone.Annotations,
oldPvcClone.Spec.StorageClassName, newPvcClone.Spec.StorageClassName) {
newPvcClone.Spec.StorageClassName = nil
metav1.SetMetaDataAnnotation(&newPvcClone.ObjectMeta, core.BetaStorageClassAnnotation, oldPvcClone.Annotations[core.BetaStorageClassAnnotation])
} else {
// storageclass annotation should be immutable after creation
// TODO: remove Beta when no longer needed
allErrs = append(allErrs, ValidateImmutableAnnotation(newPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], oldPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], v1.BetaStorageClassAnnotation, field.NewPath("metadata"))...)
}
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
// lets make sure storage values are same.
if newPvc.Status.Phase == core.ClaimBound && newPvcClone.Spec.Resources.Requests != nil {
@ -1807,16 +1817,28 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl
}
}
// storageclass annotation should be immutable after creation
// TODO: remove Beta when no longer needed
allErrs = append(allErrs, ValidateImmutableAnnotation(newPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], oldPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], v1.BetaStorageClassAnnotation, field.NewPath("metadata"))...)
if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
allErrs = append(allErrs, ValidateImmutableField(newPvc.Spec.VolumeMode, oldPvc.Spec.VolumeMode, field.NewPath("volumeMode"))...)
}
return allErrs
}
// Provide an upgrade path from PVC with storage class specified in beta
// annotation to storage class specified in attribute. We allow update of
// StorageClassName only if following four conditions are met at the same time:
// 1. The old pvc's StorageClassAnnotation is set
// 2. The old pvc's StorageClassName is not set
// 3. The new pvc's StorageClassName is set and equal to the old value in annotation
// 4. If the new pvc's StorageClassAnnotation is set,it must be equal to the old pv/pvc's StorageClassAnnotation
func validateStorageClassUpgrade(oldAnnotations, newAnnotations map[string]string, oldScName, newScName *string) bool {
oldSc, oldAnnotationExist := oldAnnotations[core.BetaStorageClassAnnotation]
newScInAnnotation, newAnnotationExist := newAnnotations[core.BetaStorageClassAnnotation]
return oldAnnotationExist /* condition 1 */ &&
oldScName == nil /* condition 2*/ &&
(newScName != nil && *newScName == oldSc) /* condition 3 */ &&
(!newAnnotationExist || newScInAnnotation == oldSc) /* condition 4 */
}
// ValidatePersistentVolumeClaimStatusUpdate validates an update to status of a PersistentVolumeClaim
func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVolumeClaim) field.ErrorList {
allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))

View File

@ -737,6 +737,29 @@ func testVolumeClaimAnnotation(name string, namespace string, ann string, annval
}
}
func testVolumeClaimStorageClassInSpec(name, namespace, scName string, spec core.PersistentVolumeClaimSpec) *core.PersistentVolumeClaim {
spec.StorageClassName = &scName
return &core.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: spec,
}
}
func testVolumeClaimStorageClassInAnnotationAndSpec(name, namespace, scNameInAnn, scName string, spec core.PersistentVolumeClaimSpec) *core.PersistentVolumeClaim {
spec.StorageClassName = &scName
return &core.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Annotations: map[string]string{v1.BetaStorageClassAnnotation: scNameInAnn},
},
Spec: spec,
}
}
func TestValidatePersistentVolumeClaim(t *testing.T) {
invalidClassName := "-invalid-"
validClassName := "valid"
@ -1252,6 +1275,52 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
Phase: core.ClaimPending,
})
validClaimStorageClassInSpec := testVolumeClaimStorageClassInSpec("foo", "ns", "fast", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadOnlyMany,
},
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
},
})
invalidClaimStorageClassInSpec := testVolumeClaimStorageClassInSpec("foo", "ns", "fast2", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadOnlyMany,
},
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
},
})
validClaimStorageClassInAnnotationAndSpec := testVolumeClaimStorageClassInAnnotationAndSpec(
"foo", "ns", "fast", "fast", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadOnlyMany,
},
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
},
})
invalidClaimStorageClassInAnnotationAndSpec := testVolumeClaimStorageClassInAnnotationAndSpec(
"foo", "ns", "fast2", "fast", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadOnlyMany,
},
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
},
})
scenarios := map[string]struct {
isExpectedFailure bool
oldClaim *core.PersistentVolumeClaim
@ -1413,6 +1482,48 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
enableResize: true,
enableBlock: false,
},
"valid-upgrade-storage-class-annotation-to-spec": {
isExpectedFailure: false,
oldClaim: validClaimStorageClass,
newClaim: validClaimStorageClassInSpec,
enableResize: false,
enableBlock: false,
},
"invalid-upgrade-storage-class-annotation-to-spec": {
isExpectedFailure: true,
oldClaim: validClaimStorageClass,
newClaim: invalidClaimStorageClassInSpec,
enableResize: false,
enableBlock: false,
},
"valid-upgrade-storage-class-annotation-to-annotation-and-spec": {
isExpectedFailure: false,
oldClaim: validClaimStorageClass,
newClaim: validClaimStorageClassInAnnotationAndSpec,
enableResize: false,
enableBlock: false,
},
"invalid-upgrade-storage-class-annotation-to-annotation-and-spec": {
isExpectedFailure: true,
oldClaim: validClaimStorageClass,
newClaim: invalidClaimStorageClassInAnnotationAndSpec,
enableResize: false,
enableBlock: false,
},
"invalid-upgrade-storage-class-in-spec": {
isExpectedFailure: true,
oldClaim: validClaimStorageClassInSpec,
newClaim: invalidClaimStorageClassInSpec,
enableResize: false,
enableBlock: false,
},
"invalid-downgrade-storage-class-spec-to-annotation": {
isExpectedFailure: true,
oldClaim: validClaimStorageClassInSpec,
newClaim: validClaimStorageClass,
enableResize: false,
enableBlock: false,
},
}
for name, scenario := range scenarios {