mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
remove ExpandPersistentVolume feature gate
This commit is contained in:
parent
6d071d6593
commit
9343cce20b
@ -65,7 +65,6 @@ import (
|
|||||||
persistentvolumecontroller "k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
|
persistentvolumecontroller "k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/pvcprotection"
|
"k8s.io/kubernetes/pkg/controller/volume/pvcprotection"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/pvprotection"
|
"k8s.io/kubernetes/pkg/controller/volume/pvprotection"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
|
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
|
||||||
"k8s.io/kubernetes/pkg/volume/csimigration"
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
@ -336,36 +335,34 @@ func startAttachDetachController(ctx context.Context, controllerContext Controll
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startVolumeExpandController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
|
func startVolumeExpandController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
plugins, err := ProbeExpandableVolumePlugins(controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration)
|
||||||
plugins, err := ProbeExpandableVolumePlugins(controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, true, fmt.Errorf("failed to probe volume plugins when starting volume expand controller: %v", err)
|
||||||
return nil, true, fmt.Errorf("failed to probe volume plugins when starting volume expand controller: %v", err)
|
|
||||||
}
|
|
||||||
csiTranslator := csitrans.New()
|
|
||||||
filteredDialOptions, err := options.ParseVolumeHostFilters(
|
|
||||||
controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeHostCIDRDenylist,
|
|
||||||
controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeHostAllowLocalLoopback)
|
|
||||||
if err != nil {
|
|
||||||
return nil, true, err
|
|
||||||
}
|
|
||||||
expandController, expandControllerErr := expand.NewExpandController(
|
|
||||||
controllerContext.ClientBuilder.ClientOrDie("expand-controller"),
|
|
||||||
controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(),
|
|
||||||
controllerContext.InformerFactory.Core().V1().PersistentVolumes(),
|
|
||||||
controllerContext.Cloud,
|
|
||||||
plugins,
|
|
||||||
csiTranslator,
|
|
||||||
csimigration.NewPluginManager(csiTranslator, utilfeature.DefaultFeatureGate),
|
|
||||||
filteredDialOptions,
|
|
||||||
)
|
|
||||||
|
|
||||||
if expandControllerErr != nil {
|
|
||||||
return nil, true, fmt.Errorf("failed to start volume expand controller: %v", expandControllerErr)
|
|
||||||
}
|
|
||||||
go expandController.Run(ctx)
|
|
||||||
return nil, true, nil
|
|
||||||
}
|
}
|
||||||
return nil, false, nil
|
csiTranslator := csitrans.New()
|
||||||
|
filteredDialOptions, err := options.ParseVolumeHostFilters(
|
||||||
|
controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeHostCIDRDenylist,
|
||||||
|
controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeHostAllowLocalLoopback)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
expandController, expandControllerErr := expand.NewExpandController(
|
||||||
|
controllerContext.ClientBuilder.ClientOrDie("expand-controller"),
|
||||||
|
controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(),
|
||||||
|
controllerContext.InformerFactory.Core().V1().PersistentVolumes(),
|
||||||
|
controllerContext.Cloud,
|
||||||
|
plugins,
|
||||||
|
csiTranslator,
|
||||||
|
csimigration.NewPluginManager(csiTranslator, utilfeature.DefaultFeatureGate),
|
||||||
|
filteredDialOptions,
|
||||||
|
)
|
||||||
|
|
||||||
|
if expandControllerErr != nil {
|
||||||
|
return nil, true, fmt.Errorf("failed to start volume expand controller: %v", expandControllerErr)
|
||||||
|
}
|
||||||
|
go expandController.Run(ctx)
|
||||||
|
return nil, true, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startEphemeralVolumeController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
|
func startEphemeralVolumeController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
|
||||||
|
@ -75,10 +75,6 @@ func EnforceDataSourceBackwardsCompatibility(pvcSpec, oldPVCSpec *core.Persisten
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DropDisabledFieldsFromStatus(pvc, oldPVC *core.PersistentVolumeClaim) {
|
func DropDisabledFieldsFromStatus(pvc, oldPVC *core.PersistentVolumeClaim) {
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) && oldPVC.Status.Conditions == nil {
|
|
||||||
pvc.Status.Conditions = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) {
|
||||||
if !allocatedResourcesInUse(oldPVC) {
|
if !allocatedResourcesInUse(oldPVC) {
|
||||||
pvc.Status.AllocatedResources = nil
|
pvc.Status.AllocatedResources = nil
|
||||||
|
@ -2022,8 +2022,6 @@ func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *core.PersistentVolume) f
|
|||||||
type PersistentVolumeClaimSpecValidationOptions struct {
|
type PersistentVolumeClaimSpecValidationOptions struct {
|
||||||
// Allow spec to contain the "ReadWiteOncePod" access mode
|
// Allow spec to contain the "ReadWiteOncePod" access mode
|
||||||
AllowReadWriteOncePod bool
|
AllowReadWriteOncePod bool
|
||||||
// Allow pvc expansion after PVC is created and bound to a PV
|
|
||||||
EnableExpansion bool
|
|
||||||
// Allow users to recover from previously failing expansion operation
|
// Allow users to recover from previously failing expansion operation
|
||||||
EnableRecoverFromExpansionFailure bool
|
EnableRecoverFromExpansionFailure bool
|
||||||
}
|
}
|
||||||
@ -2031,7 +2029,6 @@ type PersistentVolumeClaimSpecValidationOptions struct {
|
|||||||
func ValidationOptionsForPersistentVolumeClaim(pvc, oldPvc *core.PersistentVolumeClaim) PersistentVolumeClaimSpecValidationOptions {
|
func ValidationOptionsForPersistentVolumeClaim(pvc, oldPvc *core.PersistentVolumeClaim) PersistentVolumeClaimSpecValidationOptions {
|
||||||
opts := PersistentVolumeClaimSpecValidationOptions{
|
opts := PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: utilfeature.DefaultFeatureGate.Enabled(features.ReadWriteOncePod),
|
AllowReadWriteOncePod: utilfeature.DefaultFeatureGate.Enabled(features.ReadWriteOncePod),
|
||||||
EnableExpansion: utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes),
|
|
||||||
EnableRecoverFromExpansionFailure: utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure),
|
EnableRecoverFromExpansionFailure: utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure),
|
||||||
}
|
}
|
||||||
if oldPvc == nil {
|
if oldPvc == nil {
|
||||||
@ -2178,40 +2175,30 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl
|
|||||||
allErrs = append(allErrs, ValidateImmutableAnnotation(newPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], oldPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], v1.BetaStorageClassAnnotation, field.NewPath("metadata"))...)
|
allErrs = append(allErrs, ValidateImmutableAnnotation(newPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], oldPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], v1.BetaStorageClassAnnotation, field.NewPath("metadata"))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.EnableExpansion {
|
// lets make sure storage values are same.
|
||||||
// lets make sure storage values are same.
|
if newPvc.Status.Phase == core.ClaimBound && newPvcClone.Spec.Resources.Requests != nil {
|
||||||
if newPvc.Status.Phase == core.ClaimBound && newPvcClone.Spec.Resources.Requests != nil {
|
newPvcClone.Spec.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"] // +k8s:verify-mutation:reason=clone
|
||||||
newPvcClone.Spec.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"] // +k8s:verify-mutation:reason=clone
|
}
|
||||||
}
|
|
||||||
|
|
||||||
oldSize := oldPvc.Spec.Resources.Requests["storage"]
|
oldSize := oldPvc.Spec.Resources.Requests["storage"]
|
||||||
newSize := newPvc.Spec.Resources.Requests["storage"]
|
newSize := newPvc.Spec.Resources.Requests["storage"]
|
||||||
statusSize := oldPvc.Status.Capacity["storage"]
|
statusSize := oldPvc.Status.Capacity["storage"]
|
||||||
|
|
||||||
if !apiequality.Semantic.DeepEqual(newPvcClone.Spec, oldPvcClone.Spec) {
|
if !apiequality.Semantic.DeepEqual(newPvcClone.Spec, oldPvcClone.Spec) {
|
||||||
specDiff := cmp.Diff(oldPvcClone.Spec, newPvcClone.Spec)
|
specDiff := cmp.Diff(oldPvcClone.Spec, newPvcClone.Spec)
|
||||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), fmt.Sprintf("spec is immutable after creation except resources.requests for bound claims\n%v", specDiff)))
|
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), fmt.Sprintf("spec is immutable after creation except resources.requests for bound claims\n%v", specDiff)))
|
||||||
}
|
}
|
||||||
if newSize.Cmp(oldSize) < 0 {
|
if newSize.Cmp(oldSize) < 0 {
|
||||||
if !opts.EnableRecoverFromExpansionFailure {
|
if !opts.EnableRecoverFromExpansionFailure {
|
||||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "resources", "requests", "storage"), "field can not be less than previous value"))
|
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "resources", "requests", "storage"), "field can not be less than previous value"))
|
||||||
} else {
|
} else {
|
||||||
// This validation permits reducing pvc requested size up to capacity recorded in pvc.status
|
// This validation permits reducing pvc requested size up to capacity recorded in pvc.status
|
||||||
// so that users can recover from volume expansion failure, but Kubernetes does not actually
|
// so that users can recover from volume expansion failure, but Kubernetes does not actually
|
||||||
// support volume shrinking
|
// support volume shrinking
|
||||||
if newSize.Cmp(statusSize) <= 0 {
|
if newSize.Cmp(statusSize) <= 0 {
|
||||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "resources", "requests", "storage"), "field can not be less than status.capacity"))
|
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "resources", "requests", "storage"), "field can not be less than status.capacity"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
// changes to Spec are not allowed, but updates to label/and some annotations are OK.
|
|
||||||
// no-op updates pass validation.
|
|
||||||
if !apiequality.Semantic.DeepEqual(newPvcClone.Spec, oldPvcClone.Spec) {
|
|
||||||
specDiff := cmp.Diff(oldPvcClone.Spec, newPvcClone.Spec)
|
|
||||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), fmt.Sprintf("field is immutable after creation\n%v", specDiff)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
allErrs = append(allErrs, ValidateImmutableField(newPvc.Spec.VolumeMode, oldPvc.Spec.VolumeMode, field.NewPath("volumeMode"))...)
|
allErrs = append(allErrs, ValidateImmutableField(newPvc.Spec.VolumeMode, oldPvc.Spec.VolumeMode, field.NewPath("volumeMode"))...)
|
||||||
|
@ -2108,215 +2108,173 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||||||
isExpectedFailure bool
|
isExpectedFailure bool
|
||||||
oldClaim *core.PersistentVolumeClaim
|
oldClaim *core.PersistentVolumeClaim
|
||||||
newClaim *core.PersistentVolumeClaim
|
newClaim *core.PersistentVolumeClaim
|
||||||
enableResize bool
|
|
||||||
enableRecoverFromExpansion bool
|
enableRecoverFromExpansion bool
|
||||||
}{
|
}{
|
||||||
"valid-update-volumeName-only": {
|
"valid-update-volumeName-only": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: validUpdateClaim,
|
newClaim: validUpdateClaim,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-no-op-update": {
|
"valid-no-op-update": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validUpdateClaim,
|
oldClaim: validUpdateClaim,
|
||||||
newClaim: validUpdateClaim,
|
newClaim: validUpdateClaim,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-change-resources-on-bound-claim": {
|
"invalid-update-change-resources-on-bound-claim": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validUpdateClaim,
|
oldClaim: validUpdateClaim,
|
||||||
newClaim: invalidUpdateClaimResources,
|
newClaim: invalidUpdateClaimResources,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-change-access-modes-on-bound-claim": {
|
"invalid-update-change-access-modes-on-bound-claim": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validUpdateClaim,
|
oldClaim: validUpdateClaim,
|
||||||
newClaim: invalidUpdateClaimAccessModes,
|
newClaim: invalidUpdateClaimAccessModes,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-volume-mode-block-to-block": {
|
"valid-update-volume-mode-block-to-block": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimVolumeModeBlock,
|
oldClaim: validClaimVolumeModeBlock,
|
||||||
newClaim: validClaimVolumeModeBlock,
|
newClaim: validClaimVolumeModeBlock,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-volume-mode-file-to-file": {
|
"valid-update-volume-mode-file-to-file": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimVolumeModeFile,
|
oldClaim: validClaimVolumeModeFile,
|
||||||
newClaim: validClaimVolumeModeFile,
|
newClaim: validClaimVolumeModeFile,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-to-block": {
|
"invalid-update-volume-mode-to-block": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeFile,
|
oldClaim: validClaimVolumeModeFile,
|
||||||
newClaim: validClaimVolumeModeBlock,
|
newClaim: validClaimVolumeModeBlock,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-to-file": {
|
"invalid-update-volume-mode-to-file": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeBlock,
|
oldClaim: validClaimVolumeModeBlock,
|
||||||
newClaim: validClaimVolumeModeFile,
|
newClaim: validClaimVolumeModeFile,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-nil-to-file": {
|
"invalid-update-volume-mode-nil-to-file": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: invalidClaimVolumeModeNil,
|
oldClaim: invalidClaimVolumeModeNil,
|
||||||
newClaim: validClaimVolumeModeFile,
|
newClaim: validClaimVolumeModeFile,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-nil-to-block": {
|
"invalid-update-volume-mode-nil-to-block": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: invalidClaimVolumeModeNil,
|
oldClaim: invalidClaimVolumeModeNil,
|
||||||
newClaim: validClaimVolumeModeBlock,
|
newClaim: validClaimVolumeModeBlock,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-block-to-nil": {
|
"invalid-update-volume-mode-block-to-nil": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeBlock,
|
oldClaim: validClaimVolumeModeBlock,
|
||||||
newClaim: invalidClaimVolumeModeNil,
|
newClaim: invalidClaimVolumeModeNil,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-file-to-nil": {
|
"invalid-update-volume-mode-file-to-nil": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeFile,
|
oldClaim: validClaimVolumeModeFile,
|
||||||
newClaim: invalidClaimVolumeModeNil,
|
newClaim: invalidClaimVolumeModeNil,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-empty-to-mode": {
|
"invalid-update-volume-mode-empty-to-mode": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: validClaimVolumeModeBlock,
|
newClaim: validClaimVolumeModeBlock,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-mode-to-empty": {
|
"invalid-update-volume-mode-mode-to-empty": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeBlock,
|
oldClaim: validClaimVolumeModeBlock,
|
||||||
newClaim: validClaim,
|
newClaim: validClaim,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-change-storage-class-annotation-after-creation": {
|
"invalid-update-change-storage-class-annotation-after-creation": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: invalidUpdateClaimStorageClass,
|
newClaim: invalidUpdateClaimStorageClass,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-mutable-annotation": {
|
"valid-update-mutable-annotation": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimAnnotation,
|
oldClaim: validClaimAnnotation,
|
||||||
newClaim: validUpdateClaimMutableAnnotation,
|
newClaim: validUpdateClaimMutableAnnotation,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-add-annotation": {
|
"valid-update-add-annotation": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: validAddClaimAnnotation,
|
newClaim: validAddClaimAnnotation,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-size-update-resize-disabled": {
|
"valid-size-update-resize-disabled": {
|
||||||
isExpectedFailure: true,
|
oldClaim: validClaim,
|
||||||
oldClaim: validClaim,
|
newClaim: validSizeUpdate,
|
||||||
newClaim: validSizeUpdate,
|
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-size-update-resize-enabled": {
|
"valid-size-update-resize-enabled": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: validSizeUpdate,
|
newClaim: validSizeUpdate,
|
||||||
enableResize: true,
|
|
||||||
},
|
},
|
||||||
"invalid-size-update-resize-enabled": {
|
"invalid-size-update-resize-enabled": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: invalidSizeUpdate,
|
newClaim: invalidSizeUpdate,
|
||||||
enableResize: true,
|
|
||||||
},
|
},
|
||||||
"unbound-size-update-resize-enabled": {
|
"unbound-size-update-resize-enabled": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: unboundSizeUpdate,
|
newClaim: unboundSizeUpdate,
|
||||||
enableResize: true,
|
|
||||||
},
|
},
|
||||||
"valid-upgrade-storage-class-annotation-to-spec": {
|
"valid-upgrade-storage-class-annotation-to-spec": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: validClaimStorageClassInSpec,
|
newClaim: validClaimStorageClassInSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-upgrade-storage-class-annotation-to-spec": {
|
"invalid-upgrade-storage-class-annotation-to-spec": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: invalidClaimStorageClassInSpec,
|
newClaim: invalidClaimStorageClassInSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-upgrade-storage-class-annotation-to-annotation-and-spec": {
|
"valid-upgrade-storage-class-annotation-to-annotation-and-spec": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: validClaimStorageClassInAnnotationAndSpec,
|
newClaim: validClaimStorageClassInAnnotationAndSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-upgrade-storage-class-annotation-to-annotation-and-spec": {
|
"invalid-upgrade-storage-class-annotation-to-annotation-and-spec": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: invalidClaimStorageClassInAnnotationAndSpec,
|
newClaim: invalidClaimStorageClassInAnnotationAndSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-upgrade-storage-class-in-spec": {
|
"invalid-upgrade-storage-class-in-spec": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClassInSpec,
|
oldClaim: validClaimStorageClassInSpec,
|
||||||
newClaim: invalidClaimStorageClassInSpec,
|
newClaim: invalidClaimStorageClassInSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-downgrade-storage-class-spec-to-annotation": {
|
"invalid-downgrade-storage-class-spec-to-annotation": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClassInSpec,
|
oldClaim: validClaimStorageClassInSpec,
|
||||||
newClaim: validClaimStorageClass,
|
newClaim: validClaimStorageClass,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-rwop-used-and-rwop-feature-disabled": {
|
"valid-update-rwop-used-and-rwop-feature-disabled": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimRWOPAccessMode,
|
oldClaim: validClaimRWOPAccessMode,
|
||||||
newClaim: validClaimRWOPAccessModeAddAnnotation,
|
newClaim: validClaimRWOPAccessModeAddAnnotation,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-expand-shrink-resize-enabled": {
|
"valid-expand-shrink-resize-enabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: validClaimShrink,
|
newClaim: validClaimShrink,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
},
|
},
|
||||||
"invalid-expand-shrink-resize-enabled": {
|
"invalid-expand-shrink-resize-enabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: invalidClaimShrink,
|
newClaim: invalidClaimShrink,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
"invalid-expand-shrink-to-status-resize-enabled": {
|
"invalid-expand-shrink-to-status-resize-enabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: invalidShrinkToStatus,
|
newClaim: invalidShrinkToStatus,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
"invalid-expand-shrink-recover-disabled": {
|
"invalid-expand-shrink-recover-disabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: validClaimShrink,
|
newClaim: validClaimShrink,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: false,
|
enableRecoverFromExpansion: false,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
"invalid-expand-shrink-resize-disabled": {
|
|
||||||
oldClaim: validClaimShrinkInitial,
|
|
||||||
newClaim: validClaimShrink,
|
|
||||||
enableResize: false,
|
|
||||||
enableRecoverFromExpansion: true,
|
|
||||||
isExpectedFailure: true,
|
|
||||||
},
|
|
||||||
"unbound-size-shrink-resize-enabled": {
|
"unbound-size-shrink-resize-enabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: unboundShrink,
|
newClaim: unboundShrink,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
@ -2324,7 +2282,6 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||||||
|
|
||||||
for name, scenario := range scenarios {
|
for name, scenario := range scenarios {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, scenario.enableResize)()
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, scenario.enableRecoverFromExpansion)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, scenario.enableRecoverFromExpansion)()
|
||||||
scenario.oldClaim.ResourceVersion = "1"
|
scenario.oldClaim.ResourceVersion = "1"
|
||||||
scenario.newClaim.ResourceVersion = "1"
|
scenario.newClaim.ResourceVersion = "1"
|
||||||
@ -2351,7 +2308,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: true,
|
enableReadWriteOncePod: true,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: true,
|
AllowReadWriteOncePod: true,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2360,7 +2316,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: true,
|
enableReadWriteOncePod: true,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: true,
|
AllowReadWriteOncePod: true,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2369,7 +2324,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: false,
|
enableReadWriteOncePod: false,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: false,
|
AllowReadWriteOncePod: false,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2378,7 +2332,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: true,
|
enableReadWriteOncePod: true,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: true,
|
AllowReadWriteOncePod: true,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2387,7 +2340,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: false,
|
enableReadWriteOncePod: false,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: true,
|
AllowReadWriteOncePod: true,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DropDisabledFields removes disabled fields from the StorageClass object.
|
|
||||||
func DropDisabledFields(class, oldClass *storage.StorageClass) {
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) && !allowVolumeExpansionInUse(oldClass) {
|
|
||||||
class.AllowVolumeExpansion = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func allowVolumeExpansionInUse(oldClass *storage.StorageClass) bool {
|
|
||||||
if oldClass == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if oldClass.AllowVolumeExpansion != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/diff"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDropAllowVolumeExpansion(t *testing.T) {
|
|
||||||
allowVolumeExpansion := false
|
|
||||||
scWithoutAllowVolumeExpansion := func() *storage.StorageClass {
|
|
||||||
return &storage.StorageClass{}
|
|
||||||
}
|
|
||||||
scWithAllowVolumeExpansion := func() *storage.StorageClass {
|
|
||||||
return &storage.StorageClass{
|
|
||||||
AllowVolumeExpansion: &allowVolumeExpansion,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scInfo := []struct {
|
|
||||||
description string
|
|
||||||
hasAllowVolumeExpansion bool
|
|
||||||
sc func() *storage.StorageClass
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
description: "StorageClass Without AllowVolumeExpansion",
|
|
||||||
hasAllowVolumeExpansion: false,
|
|
||||||
sc: scWithoutAllowVolumeExpansion,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "StorageClass With AllowVolumeExpansion",
|
|
||||||
hasAllowVolumeExpansion: true,
|
|
||||||
sc: scWithAllowVolumeExpansion,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "is nil",
|
|
||||||
hasAllowVolumeExpansion: false,
|
|
||||||
sc: func() *storage.StorageClass { return nil },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, enabled := range []bool{true, false} {
|
|
||||||
for _, oldStorageClassInfo := range scInfo {
|
|
||||||
for _, newStorageClassInfo := range scInfo {
|
|
||||||
oldStorageClassHasAllowVolumeExpansion, oldStorageClass := oldStorageClassInfo.hasAllowVolumeExpansion, oldStorageClassInfo.sc()
|
|
||||||
newStorageClassHasAllowVolumeExpansion, newStorageClass := newStorageClassInfo.hasAllowVolumeExpansion, newStorageClassInfo.sc()
|
|
||||||
if newStorageClass == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("feature enabled=%v, old StorageClass %v, new StorageClass %v", enabled, oldStorageClassInfo.description, newStorageClassInfo.description), func(t *testing.T) {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, enabled)()
|
|
||||||
|
|
||||||
DropDisabledFields(newStorageClass, oldStorageClass)
|
|
||||||
|
|
||||||
// old StorageClass should never be changed
|
|
||||||
if !reflect.DeepEqual(oldStorageClass, oldStorageClassInfo.sc()) {
|
|
||||||
t.Errorf("old StorageClass changed: %v", diff.ObjectReflectDiff(oldStorageClass, oldStorageClassInfo.sc()))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case enabled || oldStorageClassHasAllowVolumeExpansion:
|
|
||||||
// new StorageClass should not be changed if the feature is enabled, or if the old StorageClass had AllowVolumeExpansion
|
|
||||||
if !reflect.DeepEqual(newStorageClass, newStorageClassInfo.sc()) {
|
|
||||||
t.Errorf("new StorageClass changed: %v", diff.ObjectReflectDiff(newStorageClass, newStorageClassInfo.sc()))
|
|
||||||
}
|
|
||||||
case newStorageClassHasAllowVolumeExpansion:
|
|
||||||
// new StorageClass should be changed
|
|
||||||
if reflect.DeepEqual(newStorageClass, newStorageClassInfo.sc()) {
|
|
||||||
t.Errorf("new StorageClass was not changed")
|
|
||||||
}
|
|
||||||
// new StorageClass should not have AllowVolumeExpansion
|
|
||||||
if !reflect.DeepEqual(newStorageClass, scWithoutAllowVolumeExpansion()) {
|
|
||||||
t.Errorf("new StorageClass had StorageClassAllowVolumeExpansion: %v", diff.ObjectReflectDiff(newStorageClass, scWithoutAllowVolumeExpansion()))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// new StorageClass should not need to be changed
|
|
||||||
if !reflect.DeepEqual(newStorageClass, newStorageClassInfo.sc()) {
|
|
||||||
t.Errorf("new StorageClass changed: %v", diff.ObjectReflectDiff(newStorageClass, newStorageClassInfo.sc()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -94,7 +94,7 @@ func (p *pvcEvaluator) Handles(a admission.Attributes) bool {
|
|||||||
if op == admission.Create {
|
if op == admission.Create {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if op == admission.Update && utilfeature.DefaultFeatureGate.Enabled(k8sfeatures.ExpandPersistentVolumes) {
|
if op == admission.Update {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -76,15 +76,13 @@ func TestDropConditions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, enabled := range []bool{true, false} {
|
for _, enabled := range []bool{true} {
|
||||||
for _, oldPvcInfo := range pvcInfo {
|
for _, oldPvcInfo := range pvcInfo {
|
||||||
for _, newPvcInfo := range pvcInfo {
|
for _, newPvcInfo := range pvcInfo {
|
||||||
oldPvcHasConditins, oldPvc := oldPvcInfo.hasConditions, oldPvcInfo.pvc()
|
oldPvcHasConditins, oldPvc := oldPvcInfo.hasConditions, oldPvcInfo.pvc()
|
||||||
newPvcHasConditions, newPvc := newPvcInfo.hasConditions, newPvcInfo.pvc()
|
newPvcHasConditions, newPvc := newPvcInfo.hasConditions, newPvcInfo.pvc()
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("feature enabled=%v, old pvc %v, new pvc %v", enabled, oldPvcInfo.description, newPvcInfo.description), func(t *testing.T) {
|
t.Run(fmt.Sprintf("feature enabled=%v, old pvc %v, new pvc %v", enabled, oldPvcInfo.description, newPvcInfo.description), func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, enabled)()
|
|
||||||
|
|
||||||
StatusStrategy.PrepareForUpdate(ctx, newPvc, oldPvc)
|
StatusStrategy.PrepareForUpdate(ctx, newPvc, oldPvc)
|
||||||
|
|
||||||
// old pvc should never be changed
|
// old pvc should never be changed
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/storage/names"
|
"k8s.io/apiserver/pkg/storage/names"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
"k8s.io/kubernetes/pkg/apis/storage"
|
||||||
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/storage/validation"
|
"k8s.io/kubernetes/pkg/apis/storage/validation"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,9 +43,6 @@ func (storageClassStrategy) NamespaceScoped() bool {
|
|||||||
|
|
||||||
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
|
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
|
||||||
func (storageClassStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
func (storageClassStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||||
class := obj.(*storage.StorageClass)
|
|
||||||
|
|
||||||
storageutil.DropDisabledFields(class, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (storageClassStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (storageClassStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
@ -69,10 +65,6 @@ func (storageClassStrategy) AllowCreateOnUpdate() bool {
|
|||||||
|
|
||||||
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a PV
|
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a PV
|
||||||
func (storageClassStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
func (storageClassStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||||
newClass := obj.(*storage.StorageClass)
|
|
||||||
oldClass := old.(*storage.StorageClass)
|
|
||||||
|
|
||||||
storageutil.DropDisabledFields(oldClass, newClass)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (storageClassStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (storageClassStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
|
@ -2025,10 +2025,6 @@ func (og *operationGenerator) nodeExpandVolume(
|
|||||||
volumeToMount VolumeToMount,
|
volumeToMount VolumeToMount,
|
||||||
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
||||||
rsOpts volume.NodeResizeOptions) (bool, error) {
|
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 &&
|
if volumeToMount.VolumeSpec != nil &&
|
||||||
volumeToMount.VolumeSpec.InlineVolumeSpecForCSIMigration {
|
volumeToMount.VolumeSpec.InlineVolumeSpecForCSIMigration {
|
||||||
|
@ -71,8 +71,7 @@ type Plugin struct {
|
|||||||
podsGetter corev1lister.PodLister
|
podsGetter corev1lister.PodLister
|
||||||
nodesGetter corev1lister.NodeLister
|
nodesGetter corev1lister.NodeLister
|
||||||
|
|
||||||
expandPersistentVolumesEnabled bool
|
expansionRecoveryEnabled bool
|
||||||
expansionRecoveryEnabled bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -83,7 +82,6 @@ var (
|
|||||||
|
|
||||||
// InspectFeatureGates allows setting bools without taking a dep on a global variable
|
// InspectFeatureGates allows setting bools without taking a dep on a global variable
|
||||||
func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
|
func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
|
||||||
p.expandPersistentVolumesEnabled = featureGates.Enabled(features.ExpandPersistentVolumes)
|
|
||||||
p.expansionRecoveryEnabled = featureGates.Enabled(features.RecoverVolumeExpansionFailure)
|
p.expansionRecoveryEnabled = featureGates.Enabled(features.RecoverVolumeExpansionFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,10 +334,6 @@ func (p *Plugin) admitPodEviction(nodeName string, a admission.Attributes) error
|
|||||||
func (p *Plugin) admitPVCStatus(nodeName string, a admission.Attributes) error {
|
func (p *Plugin) admitPVCStatus(nodeName string, a admission.Attributes) error {
|
||||||
switch a.GetOperation() {
|
switch a.GetOperation() {
|
||||||
case admission.Update:
|
case admission.Update:
|
||||||
if !p.expandPersistentVolumesEnabled {
|
|
||||||
return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to update persistentvolumeclaim metadata", nodeName))
|
|
||||||
}
|
|
||||||
|
|
||||||
oldPVC, ok := a.GetOldObject().(*api.PersistentVolumeClaim)
|
oldPVC, ok := a.GetOldObject().(*api.PersistentVolumeClaim)
|
||||||
if !ok {
|
if !ok {
|
||||||
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetOldObject()))
|
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetOldObject()))
|
||||||
|
@ -18,14 +18,15 @@ package noderestriction
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/util/feature"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -1527,8 +1528,6 @@ func TestAdmitPVCStatus(t *testing.T) {
|
|||||||
attributes := admission.NewAttributesRecord(
|
attributes := admission.NewAttributesRecord(
|
||||||
test.newObj, test.oldObj, schema.GroupVersionKind{},
|
test.newObj, test.oldObj, schema.GroupVersionKind{},
|
||||||
metav1.NamespaceDefault, "foo", apiResource, test.subresource, operation, &metav1.CreateOptions{}, false, mynode)
|
metav1.NamespaceDefault, "foo", apiResource, test.subresource, operation, &metav1.CreateOptions{}, false, mynode)
|
||||||
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.ExpandPersistentVolumes, test.expansionFeatureEnabled)()
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, test.recoveryFeatureEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, test.recoveryFeatureEnabled)()
|
||||||
a := &admitTestCase{
|
a := &admitTestCase{
|
||||||
name: test.name,
|
name: test.name,
|
||||||
|
@ -30,15 +30,12 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/resourcequota"
|
"k8s.io/apiserver/pkg/admission/plugin/resourcequota"
|
||||||
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
|
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
testcore "k8s.io/client-go/testing"
|
testcore "k8s.io/client-go/testing"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/quota/v1/install"
|
"k8s.io/kubernetes/pkg/quota/v1/install"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -408,8 +405,6 @@ func TestAdmitHandlesNegativePVCUpdates(t *testing.T) {
|
|||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
defer close(stopCh)
|
defer close(stopCh)
|
||||||
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, true)()
|
|
||||||
|
|
||||||
kubeClient := fake.NewSimpleClientset(resourceQuota)
|
kubeClient := fake.NewSimpleClientset(resourceQuota)
|
||||||
informerFactory := informers.NewSharedInformerFactory(kubeClient, 0)
|
informerFactory := informers.NewSharedInformerFactory(kubeClient, 0)
|
||||||
|
|
||||||
@ -458,8 +453,6 @@ func TestAdmitHandlesPVCUpdates(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, true)()
|
|
||||||
|
|
||||||
// start up quota system
|
// start up quota system
|
||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
defer close(stopCh)
|
defer close(stopCh)
|
||||||
|
@ -32,7 +32,6 @@ import (
|
|||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
storageapi "k8s.io/kubernetes/pkg/apis/storage"
|
storageapi "k8s.io/kubernetes/pkg/apis/storage"
|
||||||
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
|
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
|
||||||
"k8s.io/kubernetes/third_party/forked/gonum/graph"
|
"k8s.io/kubernetes/third_party/forked/gonum/graph"
|
||||||
"k8s.io/kubernetes/third_party/forked/gonum/graph/traverse"
|
"k8s.io/kubernetes/third_party/forked/gonum/graph/traverse"
|
||||||
@ -112,10 +111,8 @@ func (r *NodeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attribu
|
|||||||
case configMapResource:
|
case configMapResource:
|
||||||
return r.authorizeReadNamespacedObject(nodeName, configMapVertexType, attrs)
|
return r.authorizeReadNamespacedObject(nodeName, configMapVertexType, attrs)
|
||||||
case pvcResource:
|
case pvcResource:
|
||||||
if r.features.Enabled(features.ExpandPersistentVolumes) {
|
if attrs.GetSubresource() == "status" {
|
||||||
if attrs.GetSubresource() == "status" {
|
return r.authorizeStatusUpdate(nodeName, pvcVertexType, attrs)
|
||||||
return r.authorizeStatusUpdate(nodeName, pvcVertexType, attrs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return r.authorizeGet(nodeName, pvcVertexType, attrs)
|
return r.authorizeGet(nodeName, pvcVertexType, attrs)
|
||||||
case pvResource:
|
case pvResource:
|
||||||
|
@ -177,21 +177,19 @@ func buildControllerRoles() ([]rbacv1.ClusterRole, []rbacv1.ClusterRoleBinding)
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
addControllerRole(&controllerRoles, &controllerRoleBindings, rbacv1.ClusterRole{
|
||||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbacv1.ClusterRole{
|
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "expand-controller"},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "expand-controller"},
|
Rules: []rbacv1.PolicyRule{
|
||||||
Rules: []rbacv1.PolicyRule{
|
rbacv1helpers.NewRule("get", "list", "watch", "update", "patch").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get", "list", "watch", "update", "patch").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
|
rbacv1helpers.NewRule("update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie(),
|
rbacv1helpers.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
// glusterfs
|
||||||
// glusterfs
|
rbacv1helpers.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
|
rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
eventsRule(),
|
||||||
eventsRule(),
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbacv1.ClusterRole{
|
addControllerRole(&controllerRoles, &controllerRoleBindings, rbacv1.ClusterRole{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "ephemeral-volume-controller"},
|
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "ephemeral-volume-controller"},
|
||||||
|
@ -24,9 +24,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
|
rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Write and other vars are slices of the allowed verbs.
|
// Write and other vars are slices of the allowed verbs.
|
||||||
@ -161,12 +159,10 @@ func NodeRules() []rbacv1.PolicyRule {
|
|||||||
rbacv1helpers.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts/token").RuleOrDie(),
|
rbacv1helpers.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts/token").RuleOrDie(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
// Use the Node authorization mode to limit a node to update status of pvc objects referenced by pods bound to itself.
|
||||||
// Use the Node authorization mode to limit a node to update status of pvc objects referenced by pods bound to itself.
|
// Use the NodeRestriction admission plugin to limit a node to just update the status stanza.
|
||||||
// Use the NodeRestriction admission plugin to limit a node to just update the status stanza.
|
pvcStatusPolicyRule := rbacv1helpers.NewRule("get", "update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie()
|
||||||
pvcStatusPolicyRule := rbacv1helpers.NewRule("get", "update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie()
|
nodePolicyRules = append(nodePolicyRules, pvcStatusPolicyRule)
|
||||||
nodePolicyRules = append(nodePolicyRules, pvcStatusPolicyRule)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CSI
|
// CSI
|
||||||
csiDriverRule := rbacv1helpers.NewRule("get", "watch", "list").Groups("storage.k8s.io").Resources("csidrivers").RuleOrDie()
|
csiDriverRule := rbacv1helpers.NewRule("get", "watch", "list").Groups("storage.k8s.io").Resources("csidrivers").RuleOrDie()
|
||||||
|
@ -567,13 +567,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
// re-create a pod as an admin to add object references
|
// re-create a pod as an admin to add object references
|
||||||
expectAllowed(t, createNode2NormalPod(superuserClient))
|
expectAllowed(t, createNode2NormalPod(superuserClient))
|
||||||
|
|
||||||
// ExpandPersistentVolumes feature disabled
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, false)()
|
|
||||||
expectForbidden(t, updatePVCCapacity(node1Client))
|
|
||||||
expectForbidden(t, updatePVCCapacity(node2Client))
|
|
||||||
|
|
||||||
// ExpandPersistentVolumes feature enabled
|
// ExpandPersistentVolumes feature enabled
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, true)()
|
|
||||||
expectForbidden(t, updatePVCCapacity(node1Client))
|
expectForbidden(t, updatePVCCapacity(node1Client))
|
||||||
expectAllowed(t, updatePVCCapacity(node2Client))
|
expectAllowed(t, updatePVCCapacity(node2Client))
|
||||||
expectForbidden(t, updatePVCPhase(node2Client))
|
expectForbidden(t, updatePVCPhase(node2Client))
|
||||||
|
Loading…
Reference in New Issue
Block a user