Merge pull request #108929 from gnufied/move-expansion-feature-gate-ga

Move all volume expansion feature gates to GA
This commit is contained in:
Kubernetes Prow Robot 2022-03-25 18:08:16 -07:00 committed by GitHub
commit c239b406f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 130 additions and 616 deletions

View File

@ -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) {

View File

@ -1,5 +0,0 @@
# See the OWNERS docs at https://go.k8s.io/owners
reviewers:
- smarterclayton
- jsafrane

View File

@ -1,44 +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 persistentvolume
import (
utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
)
// DropDisabledFields removes disabled fields from the pv spec.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pv spec.
func DropDisabledFields(pvSpec *api.PersistentVolumeSpec, oldPVSpec *api.PersistentVolumeSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandCSIVolumes) && !hasExpansionSecrets(oldPVSpec) {
if pvSpec.CSI != nil {
pvSpec.CSI.ControllerExpandSecretRef = nil
}
}
}
func hasExpansionSecrets(oldPVSpec *api.PersistentVolumeSpec) bool {
if oldPVSpec == nil || oldPVSpec.CSI == nil {
return false
}
if oldPVSpec.CSI.ControllerExpandSecretRef != nil {
return true
}
return false
}

View File

@ -1,117 +0,0 @@
/*
Copyright 2018 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 persistentvolume
import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
)
func TestDropDisabledFields(t *testing.T) {
secretRef := &api.SecretReference{
Name: "expansion-secret",
Namespace: "default",
}
tests := map[string]struct {
oldSpec *api.PersistentVolumeSpec
newSpec *api.PersistentVolumeSpec
expectOldSpec *api.PersistentVolumeSpec
expectNewSpec *api.PersistentVolumeSpec
csiExpansionEnabled bool
}{
"disabled csi expansion clears secrets": {
csiExpansionEnabled: false,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(nil),
oldSpec: nil,
expectOldSpec: nil,
},
"enabled csi expansion preserve secrets": {
csiExpansionEnabled: true,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(secretRef),
oldSpec: nil,
expectOldSpec: nil,
},
"enabled csi expansion preserve secrets when both old and new have it": {
csiExpansionEnabled: true,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(secretRef),
oldSpec: specWithCSISecrets(secretRef),
expectOldSpec: specWithCSISecrets(secretRef),
},
"disabled csi expansion old pv had secrets": {
csiExpansionEnabled: false,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(secretRef),
oldSpec: specWithCSISecrets(secretRef),
expectOldSpec: specWithCSISecrets(secretRef),
},
"enabled csi expansion preserves secrets when old pv did not had secrets": {
csiExpansionEnabled: true,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(secretRef),
oldSpec: specWithCSISecrets(nil),
expectOldSpec: specWithCSISecrets(nil),
},
"disabled csi expansion clears secrets when old pv did not had secrets": {
csiExpansionEnabled: false,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(nil),
oldSpec: specWithCSISecrets(nil),
expectOldSpec: specWithCSISecrets(nil),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandCSIVolumes, tc.csiExpansionEnabled)()
DropDisabledFields(tc.newSpec, tc.oldSpec)
if !reflect.DeepEqual(tc.newSpec, tc.expectNewSpec) {
t.Error(cmp.Diff(tc.newSpec, tc.expectNewSpec))
}
if !reflect.DeepEqual(tc.oldSpec, tc.expectOldSpec) {
t.Error(cmp.Diff(tc.oldSpec, tc.expectOldSpec))
}
})
}
}
func specWithCSISecrets(secret *api.SecretReference) *api.PersistentVolumeSpec {
pvSpec := &api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
CSI: &api.CSIPersistentVolumeSource{
Driver: "com.google.gcepd",
VolumeHandle: "foobar",
},
},
}
if secret != nil {
pvSpec.CSI.ControllerExpandSecretRef = secret
}
return pvSpec
}

View File

@ -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

View File

@ -2019,8 +2019,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
} }
@ -2028,7 +2026,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 {
@ -2175,40 +2172,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"))...)

View File

@ -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,
}, },
}, },

View File

@ -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
}

View File

@ -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()))
}
}
})
}
}
}
}

View File

@ -78,17 +78,20 @@ const (
// owner: @gnufied // owner: @gnufied
// beta: v1.11 // beta: v1.11
// GA: 1.24
// Ability to Expand persistent volumes // Ability to Expand persistent volumes
ExpandPersistentVolumes featuregate.Feature = "ExpandPersistentVolumes" ExpandPersistentVolumes featuregate.Feature = "ExpandPersistentVolumes"
// owner: @mlmhl // owner: @mlmhl @gnufied
// beta: v1.15 // beta: v1.15
// GA: 1.24
// Ability to expand persistent volumes' file system without unmounting volumes. // Ability to expand persistent volumes' file system without unmounting volumes.
ExpandInUsePersistentVolumes featuregate.Feature = "ExpandInUsePersistentVolumes" ExpandInUsePersistentVolumes featuregate.Feature = "ExpandInUsePersistentVolumes"
// owner: @gnufied // owner: @gnufied
// alpha: v1.14 // alpha: v1.14
// beta: v1.16 // beta: v1.16
// GA: 1.24
// Ability to expand CSI volumes // Ability to expand CSI volumes
ExpandCSIVolumes featuregate.Feature = "ExpandCSIVolumes" ExpandCSIVolumes featuregate.Feature = "ExpandCSIVolumes"
@ -865,9 +868,9 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
LocalStorageCapacityIsolation: {Default: true, PreRelease: featuregate.Beta}, LocalStorageCapacityIsolation: {Default: true, PreRelease: featuregate.Beta},
EphemeralContainers: {Default: true, PreRelease: featuregate.Beta}, EphemeralContainers: {Default: true, PreRelease: featuregate.Beta},
QOSReserved: {Default: false, PreRelease: featuregate.Alpha}, QOSReserved: {Default: false, PreRelease: featuregate.Alpha},
ExpandPersistentVolumes: {Default: true, PreRelease: featuregate.Beta}, ExpandPersistentVolumes: {Default: true, PreRelease: featuregate.GA}, // remove in 1.25
ExpandInUsePersistentVolumes: {Default: true, PreRelease: featuregate.Beta}, ExpandInUsePersistentVolumes: {Default: true, PreRelease: featuregate.GA}, // remove in 1.25
ExpandCSIVolumes: {Default: true, PreRelease: featuregate.Beta}, ExpandCSIVolumes: {Default: true, PreRelease: featuregate.GA}, // remove in 1.25
CPUManager: {Default: true, PreRelease: featuregate.Beta}, CPUManager: {Default: true, PreRelease: featuregate.Beta},
MemoryManager: {Default: true, PreRelease: featuregate.Beta}, MemoryManager: {Default: true, PreRelease: featuregate.Beta},
CPUCFSQuotaPeriod: {Default: false, PreRelease: featuregate.Alpha}, CPUCFSQuotaPeriod: {Default: false, PreRelease: featuregate.Alpha},

View File

@ -26,9 +26,7 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util"
"k8s.io/kubernetes/pkg/volume/util/operationexecutor" "k8s.io/kubernetes/pkg/volume/util/operationexecutor"
@ -714,8 +712,7 @@ func (asw *actualStateOfWorld) PodExistsInVolume(
return true, volumeObj.devicePath, newRemountRequiredError(volumeObj.volumeName, podObj.podName) return true, volumeObj.devicePath, newRemountRequiredError(volumeObj.volumeName, podObj.podName)
} }
if podObj.fsResizeRequired && if podObj.fsResizeRequired &&
!volumeObj.volumeInUseErrorForExpansion && !volumeObj.volumeInUseErrorForExpansion {
utilfeature.DefaultFeatureGate.Enabled(features.ExpandInUsePersistentVolumes) {
return true, volumeObj.devicePath, newFsResizeRequiredError(volumeObj.volumeName, podObj.podName) return true, volumeObj.devicePath, newFsResizeRequiredError(volumeObj.volumeName, podObj.podName)
} }
} }

View File

@ -34,10 +34,8 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/component-helpers/storage/ephemeral" "k8s.io/component-helpers/storage/ephemeral"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/config" "k8s.io/kubernetes/pkg/kubelet/config"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/pod" "k8s.io/kubernetes/pkg/kubelet/pod"
@ -188,15 +186,13 @@ func (dswp *desiredStateOfWorldPopulator) populatorLoop() {
func (dswp *desiredStateOfWorldPopulator) findAndAddNewPods() { func (dswp *desiredStateOfWorldPopulator) findAndAddNewPods() {
// Map unique pod name to outer volume name to MountedVolume. // Map unique pod name to outer volume name to MountedVolume.
mountedVolumesForPod := make(map[volumetypes.UniquePodName]map[string]cache.MountedVolume) mountedVolumesForPod := make(map[volumetypes.UniquePodName]map[string]cache.MountedVolume)
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandInUsePersistentVolumes) { for _, mountedVolume := range dswp.actualStateOfWorld.GetMountedVolumes() {
for _, mountedVolume := range dswp.actualStateOfWorld.GetMountedVolumes() { mountedVolumes, exist := mountedVolumesForPod[mountedVolume.PodName]
mountedVolumes, exist := mountedVolumesForPod[mountedVolume.PodName] if !exist {
if !exist { mountedVolumes = make(map[string]cache.MountedVolume)
mountedVolumes = make(map[string]cache.MountedVolume) mountedVolumesForPod[mountedVolume.PodName] = mountedVolumes
mountedVolumesForPod[mountedVolume.PodName] = mountedVolumes
}
mountedVolumes[mountedVolume.OuterVolumeSpecName] = mountedVolume
} }
mountedVolumes[mountedVolume.OuterVolumeSpecName] = mountedVolume
} }
processedVolumesForFSResize := sets.NewString() processedVolumesForFSResize := sets.NewString()
@ -288,7 +284,6 @@ func (dswp *desiredStateOfWorldPopulator) processPodVolumes(
allVolumesAdded := true allVolumesAdded := true
mounts, devices := util.GetPodVolumeNames(pod) mounts, devices := util.GetPodVolumeNames(pod)
expandInUsePV := utilfeature.DefaultFeatureGate.Enabled(features.ExpandInUsePersistentVolumes)
// Process volume spec for each volume defined in pod // Process volume spec for each volume defined in pod
for _, podVolume := range pod.Spec.Volumes { for _, podVolume := range pod.Spec.Volumes {
if !mounts.Has(podVolume.Name) && !devices.Has(podVolume.Name) { if !mounts.Has(podVolume.Name) && !devices.Has(podVolume.Name) {
@ -319,10 +314,9 @@ func (dswp *desiredStateOfWorldPopulator) processPodVolumes(
// sync reconstructed volume // sync reconstructed volume
dswp.actualStateOfWorld.SyncReconstructedVolume(uniqueVolumeName, uniquePodName, podVolume.Name) dswp.actualStateOfWorld.SyncReconstructedVolume(uniqueVolumeName, uniquePodName, podVolume.Name)
if expandInUsePV { dswp.checkVolumeFSResize(pod, podVolume, pvc, volumeSpec,
dswp.checkVolumeFSResize(pod, podVolume, pvc, volumeSpec, uniquePodName, mountedVolumesForPod, processedVolumesForFSResize)
uniquePodName, mountedVolumesForPod, processedVolumesForFSResize)
}
} }
// some of the volume additions may have failed, should not mark this pod as fully processed // some of the volume additions may have failed, should not mark this pod as fully processed

View File

@ -31,9 +31,7 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
featuregatetesting "k8s.io/component-base/featuregate/testing"
csitrans "k8s.io/csi-translation-lib" csitrans "k8s.io/csi-translation-lib"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/configmap" "k8s.io/kubernetes/pkg/kubelet/configmap"
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
kubepod "k8s.io/kubernetes/pkg/kubelet/pod" kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
@ -911,11 +909,10 @@ func TestCheckVolumeFSResize(t *testing.T) {
} }
testcases := []struct { testcases := []struct {
resize func(*testing.T, *v1.PersistentVolume, *v1.PersistentVolumeClaim, *desiredStateOfWorldPopulator) resize func(*testing.T, *v1.PersistentVolume, *v1.PersistentVolumeClaim, *desiredStateOfWorldPopulator)
verify func(*testing.T, []v1.UniqueVolumeName, v1.UniqueVolumeName) verify func(*testing.T, []v1.UniqueVolumeName, v1.UniqueVolumeName)
enableResize bool readOnlyVol bool
readOnlyVol bool volumeMode v1.PersistentVolumeMode
volumeMode v1.PersistentVolumeMode
}{ }{
{ {
// No resize request for volume, volumes in ASW shouldn't be marked as fsResizeRequired // No resize request for volume, volumes in ASW shouldn't be marked as fsResizeRequired
@ -926,22 +923,9 @@ func TestCheckVolumeFSResize(t *testing.T) {
t.Errorf("No resize request for any volumes, but found resize required volumes in ASW: %v", vols) t.Errorf("No resize request for any volumes, but found resize required volumes in ASW: %v", vols)
} }
}, },
enableResize: true, volumeMode: v1.PersistentVolumeFilesystem,
volumeMode: v1.PersistentVolumeFilesystem,
},
{
// Disable the feature gate, so volume shouldn't be marked as fsResizeRequired
resize: func(_ *testing.T, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim, _ *desiredStateOfWorldPopulator) {
setCapacity(pv, pvc, 2)
},
verify: func(t *testing.T, vols []v1.UniqueVolumeName, _ v1.UniqueVolumeName) {
if len(vols) > 0 {
t.Errorf("Feature gate disabled, but found resize required volumes in ASW: %v", vols)
}
},
enableResize: false,
volumeMode: v1.PersistentVolumeFilesystem,
}, },
{ {
// Make volume used as ReadOnly, so volume shouldn't be marked as fsResizeRequired // Make volume used as ReadOnly, so volume shouldn't be marked as fsResizeRequired
resize: func(_ *testing.T, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim, _ *desiredStateOfWorldPopulator) { resize: func(_ *testing.T, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim, _ *desiredStateOfWorldPopulator) {
@ -952,9 +936,8 @@ func TestCheckVolumeFSResize(t *testing.T) {
t.Errorf("volume mounted as ReadOnly, but found resize required volumes in ASW: %v", vols) t.Errorf("volume mounted as ReadOnly, but found resize required volumes in ASW: %v", vols)
} }
}, },
readOnlyVol: true, readOnlyVol: true,
enableResize: true, volumeMode: v1.PersistentVolumeFilesystem,
volumeMode: v1.PersistentVolumeFilesystem,
}, },
{ {
// Clear ASW, so volume shouldn't be marked as fsResizeRequired because they are not mounted // Clear ASW, so volume shouldn't be marked as fsResizeRequired because they are not mounted
@ -967,8 +950,7 @@ func TestCheckVolumeFSResize(t *testing.T) {
t.Errorf("volume hasn't been mounted, but found resize required volumes in ASW: %v", vols) t.Errorf("volume hasn't been mounted, but found resize required volumes in ASW: %v", vols)
} }
}, },
enableResize: true, volumeMode: v1.PersistentVolumeFilesystem,
volumeMode: v1.PersistentVolumeFilesystem,
}, },
{ {
// volume in ASW should be marked as fsResizeRequired // volume in ASW should be marked as fsResizeRequired
@ -986,8 +968,7 @@ func TestCheckVolumeFSResize(t *testing.T) {
t.Fatalf("Mark wrong volume as fsResizeRequired: %s", vols[0]) t.Fatalf("Mark wrong volume as fsResizeRequired: %s", vols[0])
} }
}, },
enableResize: true, volumeMode: v1.PersistentVolumeFilesystem,
volumeMode: v1.PersistentVolumeFilesystem,
}, },
{ {
// volume in ASW should be marked as fsResizeRequired // volume in ASW should be marked as fsResizeRequired
@ -1005,8 +986,7 @@ func TestCheckVolumeFSResize(t *testing.T) {
t.Fatalf("Mark wrong volume as fsResizeRequired: %s", vols[0]) t.Fatalf("Mark wrong volume as fsResizeRequired: %s", vols[0])
} }
}, },
enableResize: true, volumeMode: v1.PersistentVolumeBlock,
volumeMode: v1.PersistentVolumeBlock,
}, },
} }
@ -1071,8 +1051,6 @@ func TestCheckVolumeFSResize(t *testing.T) {
reconcileASW(fakeASW, fakeDSW, t) reconcileASW(fakeASW, fakeDSW, t)
func() { func() {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandInUsePersistentVolumes, tc.enableResize)()
tc.resize(t, pv, pvc, dswp) tc.resize(t, pv, pvc, dswp)
resizeRequiredVolumes := reprocess(dswp, uniquePodName, fakeDSW, fakeASW) resizeRequiredVolumes := reprocess(dswp, uniquePodName, fakeDSW, fakeASW)

View File

@ -37,9 +37,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/config" "k8s.io/kubernetes/pkg/kubelet/config"
"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache" "k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
"k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff" "k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
@ -260,8 +258,7 @@ func (rc *reconciler) mountAttachVolumes() {
klog.V(5).InfoS(volumeToMount.GenerateMsgDetailed("operationExecutor.MountVolume started", remountingLogStr), "pod", klog.KObj(volumeToMount.Pod)) klog.V(5).InfoS(volumeToMount.GenerateMsgDetailed("operationExecutor.MountVolume started", remountingLogStr), "pod", klog.KObj(volumeToMount.Pod))
} }
} }
} else if cache.IsFSResizeRequiredError(err) && } else if cache.IsFSResizeRequiredError(err) {
utilfeature.DefaultFeatureGate.Enabled(features.ExpandInUsePersistentVolumes) {
klog.V(4).InfoS(volumeToMount.GenerateMsgDetailed("Starting operationExecutor.ExpandInUseVolume", ""), "pod", klog.KObj(volumeToMount.Pod)) klog.V(4).InfoS(volumeToMount.GenerateMsgDetailed("Starting operationExecutor.ExpandInUseVolume", ""), "pod", klog.KObj(volumeToMount.Pod))
err := rc.operationExecutor.ExpandInUseVolume( err := rc.operationExecutor.ExpandInUseVolume(
volumeToMount.VolumeToMount, volumeToMount.VolumeToMount,

View File

@ -31,13 +31,10 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
k8stypes "k8s.io/apimachinery/pkg/types" k8stypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache" "k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
volumetesting "k8s.io/kubernetes/pkg/volume/testing" volumetesting "k8s.io/kubernetes/pkg/volume/testing"
@ -1118,7 +1115,6 @@ func Test_GenerateUnmapDeviceFunc_Plugin_Not_Found(t *testing.T) {
// Mark volume as fsResizeRequired in ASW. // Mark volume as fsResizeRequired in ASW.
// Verifies volume's fsResizeRequired flag is cleared later. // Verifies volume's fsResizeRequired flag is cleared later.
func Test_Run_Positive_VolumeFSResizeControllerAttachEnabled(t *testing.T) { func Test_Run_Positive_VolumeFSResizeControllerAttachEnabled(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandInUsePersistentVolumes, true)()
blockMode := v1.PersistentVolumeBlock blockMode := v1.PersistentVolumeBlock
fsMode := v1.PersistentVolumeFilesystem fsMode := v1.PersistentVolumeFilesystem

View File

@ -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

View File

@ -28,7 +28,6 @@ import (
"k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
pvutil "k8s.io/kubernetes/pkg/api/persistentvolume"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
volumevalidation "k8s.io/kubernetes/pkg/volume/validation" volumevalidation "k8s.io/kubernetes/pkg/volume/validation"
@ -63,10 +62,6 @@ func (persistentvolumeStrategy) GetResetFields() map[fieldpath.APIVersion]*field
// 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 (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
pv := obj.(*api.PersistentVolume)
pv.Status = api.PersistentVolumeStatus{}
pvutil.DropDisabledFields(&pv.Spec, nil)
} }
func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
@ -94,8 +89,6 @@ func (persistentvolumeStrategy) PrepareForUpdate(ctx context.Context, obj, old r
newPv := obj.(*api.PersistentVolume) newPv := obj.(*api.PersistentVolume)
oldPv := old.(*api.PersistentVolume) oldPv := old.(*api.PersistentVolume)
newPv.Status = oldPv.Status newPv.Status = oldPv.Status
pvutil.DropDisabledFields(&newPv.Spec, &oldPv.Spec)
} }
func (persistentvolumeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (persistentvolumeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {

View File

@ -76,47 +76,35 @@ func TestDropConditions(t *testing.T) {
}, },
} }
for _, enabled := range []bool{true, false} { 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("old pvc %s, new pvc %s", 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
if !reflect.DeepEqual(oldPvc, oldPvcInfo.pvc()) {
t.Errorf("old pvc changed: %v", diff.ObjectReflectDiff(oldPvc, oldPvcInfo.pvc()))
}
// old pvc should never be changed switch {
if !reflect.DeepEqual(oldPvc, oldPvcInfo.pvc()) { case oldPvcHasConditins || newPvcHasConditions:
t.Errorf("old pvc changed: %v", diff.ObjectReflectDiff(oldPvc, oldPvcInfo.pvc())) // new pvc should not be changed if the feature is enabled, or if the old pvc had Conditions
if !reflect.DeepEqual(newPvc, newPvcInfo.pvc()) {
t.Errorf("new pvc changed: %v", diff.ObjectReflectDiff(newPvc, newPvcInfo.pvc()))
} }
default:
switch { // new pvc should not need to be changed
case enabled || oldPvcHasConditins: if !reflect.DeepEqual(newPvc, newPvcInfo.pvc()) {
// new pvc should not be changed if the feature is enabled, or if the old pvc had Conditions t.Errorf("new pvc changed: %v", diff.ObjectReflectDiff(newPvc, newPvcInfo.pvc()))
if !reflect.DeepEqual(newPvc, newPvcInfo.pvc()) {
t.Errorf("new pvc changed: %v", diff.ObjectReflectDiff(newPvc, newPvcInfo.pvc()))
}
case newPvcHasConditions:
// new pvc should be changed
if reflect.DeepEqual(newPvc, newPvcInfo.pvc()) {
t.Errorf("new pvc was not changed")
}
// new pvc should not have Conditions
if !reflect.DeepEqual(newPvc, pvcWithoutConditions()) {
t.Errorf("new pvc had Conditions: %v", diff.ObjectReflectDiff(newPvc, pvcWithoutConditions()))
}
default:
// new pvc should not need to be changed
if !reflect.DeepEqual(newPvc, newPvcInfo.pvc()) {
t.Errorf("new pvc changed: %v", diff.ObjectReflectDiff(newPvc, newPvcInfo.pvc()))
}
} }
}) }
} })
} }
} }
} }
func TestPrepareForCreate(t *testing.T) { func TestPrepareForCreate(t *testing.T) {

View File

@ -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 {

View File

@ -23,9 +23,7 @@ import (
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util"
volumetypes "k8s.io/kubernetes/pkg/volume/util/types" volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
@ -34,13 +32,6 @@ import (
var _ volume.NodeExpandableVolumePlugin = &csiPlugin{} var _ volume.NodeExpandableVolumePlugin = &csiPlugin{}
func (c *csiPlugin) RequiresFSResize() bool { func (c *csiPlugin) RequiresFSResize() bool {
// We could check plugin's node capability but we instead are going to rely on
// NodeExpand to do the right thing and return early if plugin does not have
// node expansion capability.
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandCSIVolumes) {
klog.V(4).Infof("Resizing is not enabled for CSI volume")
return false
}
return true return true
} }

View File

@ -2026,10 +2026,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 {

View File

@ -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()))

View File

@ -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,

View File

@ -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)

View File

@ -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:

View File

@ -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"},

View File

@ -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()

View File

@ -39,7 +39,7 @@ import (
imageutils "k8s.io/kubernetes/test/utils/image" imageutils "k8s.io/kubernetes/test/utils/image"
) )
var _ = utils.SIGDescribe("Mounted flexvolume volume expand [Slow] [Feature:ExpandInUsePersistentVolumes]", func() { var _ = utils.SIGDescribe("[Feature:Flexvolumes] Mounted flexvolume volume expand [Slow]", func() {
var ( var (
c clientset.Interface c clientset.Interface
ns string ns string
@ -51,7 +51,7 @@ var _ = utils.SIGDescribe("Mounted flexvolume volume expand [Slow] [Feature:Expa
nodeKeyValueLabel map[string]string nodeKeyValueLabel map[string]string
nodeLabelValue string nodeLabelValue string
nodeKey string nodeKey string
nodeList *v1.NodeList node *v1.Node
) )
f := framework.NewDefaultFramework("mounted-flexvolume-expand") f := framework.NewDefaultFramework("mounted-flexvolume-expand")
@ -63,8 +63,9 @@ var _ = utils.SIGDescribe("Mounted flexvolume volume expand [Slow] [Feature:Expa
c = f.ClientSet c = f.ClientSet
ns = f.Namespace.Name ns = f.Namespace.Name
framework.ExpectNoError(framework.WaitForAllNodesSchedulable(c, framework.TestContext.NodeSchedulableTimeout)) framework.ExpectNoError(framework.WaitForAllNodesSchedulable(c, framework.TestContext.NodeSchedulableTimeout))
var err error
node, err := e2enode.GetRandomReadySchedulableNode(f.ClientSet) node, err = e2enode.GetRandomReadySchedulableNode(f.ClientSet)
framework.ExpectNoError(err) framework.ExpectNoError(err)
nodeName = node.Name nodeName = node.Name
@ -125,9 +126,8 @@ var _ = utils.SIGDescribe("Mounted flexvolume volume expand [Slow] [Feature:Expa
driver := "dummy-attachable" driver := "dummy-attachable"
node := nodeList.Items[0]
ginkgo.By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driver)) ginkgo.By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driver))
installFlex(c, &node, "k8s", driver, path.Join(driverDir, driver)) installFlex(c, node, "k8s", driver, path.Join(driverDir, driver))
ginkgo.By(fmt.Sprintf("installing flexvolume %s on (master) node %s as %s", path.Join(driverDir, driver), node.Name, driver)) ginkgo.By(fmt.Sprintf("installing flexvolume %s on (master) node %s as %s", path.Join(driverDir, driver), node.Name, driver))
installFlex(c, nil, "k8s", driver, path.Join(driverDir, driver)) installFlex(c, nil, "k8s", driver, path.Join(driverDir, driver))

View File

@ -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))