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,7 +335,6 @@ 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)
@ -364,8 +362,7 @@ func startVolumeExpandController(ctx context.Context, controllerContext Controll
} }
go expandController.Run(ctx) go expandController.Run(ctx)
return nil, true, nil return nil, true, nil
}
return nil, false, 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,7 +2172,6 @@ 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
@ -2202,15 +2198,6 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl
} }
} }
} 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"))...)
return allErrs return allErrs

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,7 +186,6 @@ 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 {
@ -197,7 +194,6 @@ func (dswp *desiredStateOfWorldPopulator) findAndAddNewPods() {
} }
mountedVolumes[mountedVolume.OuterVolumeSpecName] = mountedVolume mountedVolumes[mountedVolume.OuterVolumeSpecName] = mountedVolume
} }
}
processedVolumesForFSResize := sets.NewString() processedVolumesForFSResize := sets.NewString()
for _, pod := range dswp.podManager.GetPods() { for _, pod := range dswp.podManager.GetPods() {
@ -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"
@ -913,7 +911,6 @@ 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
}{ }{
@ -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,
},
{
// 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, 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) {
@ -953,7 +937,6 @@ func TestCheckVolumeFSResize(t *testing.T) {
} }
}, },
readOnlyVol: true, readOnlyVol: true,
enableResize: true,
volumeMode: v1.PersistentVolumeFilesystem, volumeMode: v1.PersistentVolumeFilesystem,
}, },
{ {
@ -967,7 +950,6 @@ 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,
}, },
{ {
@ -986,7 +968,6 @@ 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,
}, },
{ {
@ -1005,7 +986,6 @@ 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,15 +76,12 @@ 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 // old pvc should never be changed
@ -93,20 +90,11 @@ func TestDropConditions(t *testing.T) {
} }
switch { switch {
case enabled || oldPvcHasConditins: case oldPvcHasConditins || newPvcHasConditions:
// new pvc should not be changed if the feature is enabled, or if the old pvc had Conditions // new pvc should not be changed if the feature is enabled, or if the old pvc had Conditions
if !reflect.DeepEqual(newPvc, newPvcInfo.pvc()) { if !reflect.DeepEqual(newPvc, newPvcInfo.pvc()) {
t.Errorf("new pvc changed: %v", diff.ObjectReflectDiff(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: default:
// new pvc should not need to be changed // new pvc should not need to be changed
if !reflect.DeepEqual(newPvc, newPvcInfo.pvc()) { if !reflect.DeepEqual(newPvc, newPvcInfo.pvc()) {
@ -116,7 +104,7 @@ func TestDropConditions(t *testing.T) {
}) })
} }
} }
}
} }
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,7 +71,6 @@ type Plugin struct {
podsGetter corev1lister.PodLister podsGetter corev1lister.PodLister
nodesGetter corev1lister.NodeLister nodesGetter corev1lister.NodeLister
expandPersistentVolumesEnabled bool
expansionRecoveryEnabled bool expansionRecoveryEnabled bool
} }
@ -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,11 +111,9 @@ 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:
return r.authorizeGet(nodeName, pvVertexType, attrs) return r.authorizeGet(nodeName, pvVertexType, attrs)

View File

@ -177,7 +177,6 @@ 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{
@ -191,7 +190,6 @@ func buildControllerRoles() ([]rbacv1.ClusterRole, []rbacv1.ClusterRoleBinding)
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))