mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Merge pull request #108929 from gnufied/move-expansion-feature-gate-ga
Move all volume expansion feature gates to GA
This commit is contained in:
commit
c239b406f0
@ -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) {
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
# See the OWNERS docs at https://go.k8s.io/owners
|
|
||||||
|
|
||||||
reviewers:
|
|
||||||
- smarterclayton
|
|
||||||
- jsafrane
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -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"))...)
|
||||||
|
@ -2108,215 +2108,173 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||||||
isExpectedFailure bool
|
isExpectedFailure bool
|
||||||
oldClaim *core.PersistentVolumeClaim
|
oldClaim *core.PersistentVolumeClaim
|
||||||
newClaim *core.PersistentVolumeClaim
|
newClaim *core.PersistentVolumeClaim
|
||||||
enableResize bool
|
|
||||||
enableRecoverFromExpansion bool
|
enableRecoverFromExpansion bool
|
||||||
}{
|
}{
|
||||||
"valid-update-volumeName-only": {
|
"valid-update-volumeName-only": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: validUpdateClaim,
|
newClaim: validUpdateClaim,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-no-op-update": {
|
"valid-no-op-update": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validUpdateClaim,
|
oldClaim: validUpdateClaim,
|
||||||
newClaim: validUpdateClaim,
|
newClaim: validUpdateClaim,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-change-resources-on-bound-claim": {
|
"invalid-update-change-resources-on-bound-claim": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validUpdateClaim,
|
oldClaim: validUpdateClaim,
|
||||||
newClaim: invalidUpdateClaimResources,
|
newClaim: invalidUpdateClaimResources,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-change-access-modes-on-bound-claim": {
|
"invalid-update-change-access-modes-on-bound-claim": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validUpdateClaim,
|
oldClaim: validUpdateClaim,
|
||||||
newClaim: invalidUpdateClaimAccessModes,
|
newClaim: invalidUpdateClaimAccessModes,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-volume-mode-block-to-block": {
|
"valid-update-volume-mode-block-to-block": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimVolumeModeBlock,
|
oldClaim: validClaimVolumeModeBlock,
|
||||||
newClaim: validClaimVolumeModeBlock,
|
newClaim: validClaimVolumeModeBlock,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-volume-mode-file-to-file": {
|
"valid-update-volume-mode-file-to-file": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimVolumeModeFile,
|
oldClaim: validClaimVolumeModeFile,
|
||||||
newClaim: validClaimVolumeModeFile,
|
newClaim: validClaimVolumeModeFile,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-to-block": {
|
"invalid-update-volume-mode-to-block": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeFile,
|
oldClaim: validClaimVolumeModeFile,
|
||||||
newClaim: validClaimVolumeModeBlock,
|
newClaim: validClaimVolumeModeBlock,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-to-file": {
|
"invalid-update-volume-mode-to-file": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeBlock,
|
oldClaim: validClaimVolumeModeBlock,
|
||||||
newClaim: validClaimVolumeModeFile,
|
newClaim: validClaimVolumeModeFile,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-nil-to-file": {
|
"invalid-update-volume-mode-nil-to-file": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: invalidClaimVolumeModeNil,
|
oldClaim: invalidClaimVolumeModeNil,
|
||||||
newClaim: validClaimVolumeModeFile,
|
newClaim: validClaimVolumeModeFile,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-nil-to-block": {
|
"invalid-update-volume-mode-nil-to-block": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: invalidClaimVolumeModeNil,
|
oldClaim: invalidClaimVolumeModeNil,
|
||||||
newClaim: validClaimVolumeModeBlock,
|
newClaim: validClaimVolumeModeBlock,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-block-to-nil": {
|
"invalid-update-volume-mode-block-to-nil": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeBlock,
|
oldClaim: validClaimVolumeModeBlock,
|
||||||
newClaim: invalidClaimVolumeModeNil,
|
newClaim: invalidClaimVolumeModeNil,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-file-to-nil": {
|
"invalid-update-volume-mode-file-to-nil": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeFile,
|
oldClaim: validClaimVolumeModeFile,
|
||||||
newClaim: invalidClaimVolumeModeNil,
|
newClaim: invalidClaimVolumeModeNil,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-empty-to-mode": {
|
"invalid-update-volume-mode-empty-to-mode": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: validClaimVolumeModeBlock,
|
newClaim: validClaimVolumeModeBlock,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-volume-mode-mode-to-empty": {
|
"invalid-update-volume-mode-mode-to-empty": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimVolumeModeBlock,
|
oldClaim: validClaimVolumeModeBlock,
|
||||||
newClaim: validClaim,
|
newClaim: validClaim,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-update-change-storage-class-annotation-after-creation": {
|
"invalid-update-change-storage-class-annotation-after-creation": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: invalidUpdateClaimStorageClass,
|
newClaim: invalidUpdateClaimStorageClass,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-mutable-annotation": {
|
"valid-update-mutable-annotation": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimAnnotation,
|
oldClaim: validClaimAnnotation,
|
||||||
newClaim: validUpdateClaimMutableAnnotation,
|
newClaim: validUpdateClaimMutableAnnotation,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-add-annotation": {
|
"valid-update-add-annotation": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: validAddClaimAnnotation,
|
newClaim: validAddClaimAnnotation,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-size-update-resize-disabled": {
|
"valid-size-update-resize-disabled": {
|
||||||
isExpectedFailure: true,
|
oldClaim: validClaim,
|
||||||
oldClaim: validClaim,
|
newClaim: validSizeUpdate,
|
||||||
newClaim: validSizeUpdate,
|
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-size-update-resize-enabled": {
|
"valid-size-update-resize-enabled": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: validSizeUpdate,
|
newClaim: validSizeUpdate,
|
||||||
enableResize: true,
|
|
||||||
},
|
},
|
||||||
"invalid-size-update-resize-enabled": {
|
"invalid-size-update-resize-enabled": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: invalidSizeUpdate,
|
newClaim: invalidSizeUpdate,
|
||||||
enableResize: true,
|
|
||||||
},
|
},
|
||||||
"unbound-size-update-resize-enabled": {
|
"unbound-size-update-resize-enabled": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaim,
|
oldClaim: validClaim,
|
||||||
newClaim: unboundSizeUpdate,
|
newClaim: unboundSizeUpdate,
|
||||||
enableResize: true,
|
|
||||||
},
|
},
|
||||||
"valid-upgrade-storage-class-annotation-to-spec": {
|
"valid-upgrade-storage-class-annotation-to-spec": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: validClaimStorageClassInSpec,
|
newClaim: validClaimStorageClassInSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-upgrade-storage-class-annotation-to-spec": {
|
"invalid-upgrade-storage-class-annotation-to-spec": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: invalidClaimStorageClassInSpec,
|
newClaim: invalidClaimStorageClassInSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-upgrade-storage-class-annotation-to-annotation-and-spec": {
|
"valid-upgrade-storage-class-annotation-to-annotation-and-spec": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: validClaimStorageClassInAnnotationAndSpec,
|
newClaim: validClaimStorageClassInAnnotationAndSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-upgrade-storage-class-annotation-to-annotation-and-spec": {
|
"invalid-upgrade-storage-class-annotation-to-annotation-and-spec": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClass,
|
oldClaim: validClaimStorageClass,
|
||||||
newClaim: invalidClaimStorageClassInAnnotationAndSpec,
|
newClaim: invalidClaimStorageClassInAnnotationAndSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-upgrade-storage-class-in-spec": {
|
"invalid-upgrade-storage-class-in-spec": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClassInSpec,
|
oldClaim: validClaimStorageClassInSpec,
|
||||||
newClaim: invalidClaimStorageClassInSpec,
|
newClaim: invalidClaimStorageClassInSpec,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"invalid-downgrade-storage-class-spec-to-annotation": {
|
"invalid-downgrade-storage-class-spec-to-annotation": {
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
oldClaim: validClaimStorageClassInSpec,
|
oldClaim: validClaimStorageClassInSpec,
|
||||||
newClaim: validClaimStorageClass,
|
newClaim: validClaimStorageClass,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-update-rwop-used-and-rwop-feature-disabled": {
|
"valid-update-rwop-used-and-rwop-feature-disabled": {
|
||||||
isExpectedFailure: false,
|
isExpectedFailure: false,
|
||||||
oldClaim: validClaimRWOPAccessMode,
|
oldClaim: validClaimRWOPAccessMode,
|
||||||
newClaim: validClaimRWOPAccessModeAddAnnotation,
|
newClaim: validClaimRWOPAccessModeAddAnnotation,
|
||||||
enableResize: false,
|
|
||||||
},
|
},
|
||||||
"valid-expand-shrink-resize-enabled": {
|
"valid-expand-shrink-resize-enabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: validClaimShrink,
|
newClaim: validClaimShrink,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
},
|
},
|
||||||
"invalid-expand-shrink-resize-enabled": {
|
"invalid-expand-shrink-resize-enabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: invalidClaimShrink,
|
newClaim: invalidClaimShrink,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
"invalid-expand-shrink-to-status-resize-enabled": {
|
"invalid-expand-shrink-to-status-resize-enabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: invalidShrinkToStatus,
|
newClaim: invalidShrinkToStatus,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
"invalid-expand-shrink-recover-disabled": {
|
"invalid-expand-shrink-recover-disabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: validClaimShrink,
|
newClaim: validClaimShrink,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: false,
|
enableRecoverFromExpansion: false,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
"invalid-expand-shrink-resize-disabled": {
|
|
||||||
oldClaim: validClaimShrinkInitial,
|
|
||||||
newClaim: validClaimShrink,
|
|
||||||
enableResize: false,
|
|
||||||
enableRecoverFromExpansion: true,
|
|
||||||
isExpectedFailure: true,
|
|
||||||
},
|
|
||||||
"unbound-size-shrink-resize-enabled": {
|
"unbound-size-shrink-resize-enabled": {
|
||||||
oldClaim: validClaimShrinkInitial,
|
oldClaim: validClaimShrinkInitial,
|
||||||
newClaim: unboundShrink,
|
newClaim: unboundShrink,
|
||||||
enableResize: true,
|
|
||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
@ -2324,7 +2282,6 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||||||
|
|
||||||
for name, scenario := range scenarios {
|
for name, scenario := range scenarios {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, scenario.enableResize)()
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, scenario.enableRecoverFromExpansion)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, scenario.enableRecoverFromExpansion)()
|
||||||
scenario.oldClaim.ResourceVersion = "1"
|
scenario.oldClaim.ResourceVersion = "1"
|
||||||
scenario.newClaim.ResourceVersion = "1"
|
scenario.newClaim.ResourceVersion = "1"
|
||||||
@ -2351,7 +2308,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: true,
|
enableReadWriteOncePod: true,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: true,
|
AllowReadWriteOncePod: true,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2360,7 +2316,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: true,
|
enableReadWriteOncePod: true,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: true,
|
AllowReadWriteOncePod: true,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2369,7 +2324,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: false,
|
enableReadWriteOncePod: false,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: false,
|
AllowReadWriteOncePod: false,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2378,7 +2332,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: true,
|
enableReadWriteOncePod: true,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: true,
|
AllowReadWriteOncePod: true,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2387,7 +2340,6 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
enableReadWriteOncePod: false,
|
enableReadWriteOncePod: false,
|
||||||
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
AllowReadWriteOncePod: true,
|
AllowReadWriteOncePod: true,
|
||||||
EnableExpansion: true,
|
|
||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DropDisabledFields removes disabled fields from the StorageClass object.
|
|
||||||
func DropDisabledFields(class, oldClass *storage.StorageClass) {
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) && !allowVolumeExpansionInUse(oldClass) {
|
|
||||||
class.AllowVolumeExpansion = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func allowVolumeExpansionInUse(oldClass *storage.StorageClass) bool {
|
|
||||||
if oldClass == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if oldClass.AllowVolumeExpansion != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/diff"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDropAllowVolumeExpansion(t *testing.T) {
|
|
||||||
allowVolumeExpansion := false
|
|
||||||
scWithoutAllowVolumeExpansion := func() *storage.StorageClass {
|
|
||||||
return &storage.StorageClass{}
|
|
||||||
}
|
|
||||||
scWithAllowVolumeExpansion := func() *storage.StorageClass {
|
|
||||||
return &storage.StorageClass{
|
|
||||||
AllowVolumeExpansion: &allowVolumeExpansion,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scInfo := []struct {
|
|
||||||
description string
|
|
||||||
hasAllowVolumeExpansion bool
|
|
||||||
sc func() *storage.StorageClass
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
description: "StorageClass Without AllowVolumeExpansion",
|
|
||||||
hasAllowVolumeExpansion: false,
|
|
||||||
sc: scWithoutAllowVolumeExpansion,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "StorageClass With AllowVolumeExpansion",
|
|
||||||
hasAllowVolumeExpansion: true,
|
|
||||||
sc: scWithAllowVolumeExpansion,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "is nil",
|
|
||||||
hasAllowVolumeExpansion: false,
|
|
||||||
sc: func() *storage.StorageClass { return nil },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, enabled := range []bool{true, false} {
|
|
||||||
for _, oldStorageClassInfo := range scInfo {
|
|
||||||
for _, newStorageClassInfo := range scInfo {
|
|
||||||
oldStorageClassHasAllowVolumeExpansion, oldStorageClass := oldStorageClassInfo.hasAllowVolumeExpansion, oldStorageClassInfo.sc()
|
|
||||||
newStorageClassHasAllowVolumeExpansion, newStorageClass := newStorageClassInfo.hasAllowVolumeExpansion, newStorageClassInfo.sc()
|
|
||||||
if newStorageClass == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("feature enabled=%v, old StorageClass %v, new StorageClass %v", enabled, oldStorageClassInfo.description, newStorageClassInfo.description), func(t *testing.T) {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, enabled)()
|
|
||||||
|
|
||||||
DropDisabledFields(newStorageClass, oldStorageClass)
|
|
||||||
|
|
||||||
// old StorageClass should never be changed
|
|
||||||
if !reflect.DeepEqual(oldStorageClass, oldStorageClassInfo.sc()) {
|
|
||||||
t.Errorf("old StorageClass changed: %v", diff.ObjectReflectDiff(oldStorageClass, oldStorageClassInfo.sc()))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case enabled || oldStorageClassHasAllowVolumeExpansion:
|
|
||||||
// new StorageClass should not be changed if the feature is enabled, or if the old StorageClass had AllowVolumeExpansion
|
|
||||||
if !reflect.DeepEqual(newStorageClass, newStorageClassInfo.sc()) {
|
|
||||||
t.Errorf("new StorageClass changed: %v", diff.ObjectReflectDiff(newStorageClass, newStorageClassInfo.sc()))
|
|
||||||
}
|
|
||||||
case newStorageClassHasAllowVolumeExpansion:
|
|
||||||
// new StorageClass should be changed
|
|
||||||
if reflect.DeepEqual(newStorageClass, newStorageClassInfo.sc()) {
|
|
||||||
t.Errorf("new StorageClass was not changed")
|
|
||||||
}
|
|
||||||
// new StorageClass should not have AllowVolumeExpansion
|
|
||||||
if !reflect.DeepEqual(newStorageClass, scWithoutAllowVolumeExpansion()) {
|
|
||||||
t.Errorf("new StorageClass had StorageClassAllowVolumeExpansion: %v", diff.ObjectReflectDiff(newStorageClass, scWithoutAllowVolumeExpansion()))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// new StorageClass should not need to be changed
|
|
||||||
if !reflect.DeepEqual(newStorageClass, newStorageClassInfo.sc()) {
|
|
||||||
t.Errorf("new StorageClass changed: %v", diff.ObjectReflectDiff(newStorageClass, newStorageClassInfo.sc()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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},
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -71,8 +71,7 @@ type Plugin struct {
|
|||||||
podsGetter corev1lister.PodLister
|
podsGetter corev1lister.PodLister
|
||||||
nodesGetter corev1lister.NodeLister
|
nodesGetter corev1lister.NodeLister
|
||||||
|
|
||||||
expandPersistentVolumesEnabled bool
|
expansionRecoveryEnabled bool
|
||||||
expansionRecoveryEnabled bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -83,7 +82,6 @@ var (
|
|||||||
|
|
||||||
// InspectFeatureGates allows setting bools without taking a dep on a global variable
|
// InspectFeatureGates allows setting bools without taking a dep on a global variable
|
||||||
func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
|
func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
|
||||||
p.expandPersistentVolumesEnabled = featureGates.Enabled(features.ExpandPersistentVolumes)
|
|
||||||
p.expansionRecoveryEnabled = featureGates.Enabled(features.RecoverVolumeExpansionFailure)
|
p.expansionRecoveryEnabled = featureGates.Enabled(features.RecoverVolumeExpansionFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,10 +334,6 @@ func (p *Plugin) admitPodEviction(nodeName string, a admission.Attributes) error
|
|||||||
func (p *Plugin) admitPVCStatus(nodeName string, a admission.Attributes) error {
|
func (p *Plugin) admitPVCStatus(nodeName string, a admission.Attributes) error {
|
||||||
switch a.GetOperation() {
|
switch a.GetOperation() {
|
||||||
case admission.Update:
|
case admission.Update:
|
||||||
if !p.expandPersistentVolumesEnabled {
|
|
||||||
return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to update persistentvolumeclaim metadata", nodeName))
|
|
||||||
}
|
|
||||||
|
|
||||||
oldPVC, ok := a.GetOldObject().(*api.PersistentVolumeClaim)
|
oldPVC, ok := a.GetOldObject().(*api.PersistentVolumeClaim)
|
||||||
if !ok {
|
if !ok {
|
||||||
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetOldObject()))
|
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetOldObject()))
|
||||||
|
@ -18,14 +18,15 @@ package noderestriction
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/util/feature"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -1527,8 +1528,6 @@ func TestAdmitPVCStatus(t *testing.T) {
|
|||||||
attributes := admission.NewAttributesRecord(
|
attributes := admission.NewAttributesRecord(
|
||||||
test.newObj, test.oldObj, schema.GroupVersionKind{},
|
test.newObj, test.oldObj, schema.GroupVersionKind{},
|
||||||
metav1.NamespaceDefault, "foo", apiResource, test.subresource, operation, &metav1.CreateOptions{}, false, mynode)
|
metav1.NamespaceDefault, "foo", apiResource, test.subresource, operation, &metav1.CreateOptions{}, false, mynode)
|
||||||
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.ExpandPersistentVolumes, test.expansionFeatureEnabled)()
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, test.recoveryFeatureEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, test.recoveryFeatureEnabled)()
|
||||||
a := &admitTestCase{
|
a := &admitTestCase{
|
||||||
name: test.name,
|
name: test.name,
|
||||||
|
@ -30,15 +30,12 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/resourcequota"
|
"k8s.io/apiserver/pkg/admission/plugin/resourcequota"
|
||||||
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
|
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
testcore "k8s.io/client-go/testing"
|
testcore "k8s.io/client-go/testing"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/quota/v1/install"
|
"k8s.io/kubernetes/pkg/quota/v1/install"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -408,8 +405,6 @@ func TestAdmitHandlesNegativePVCUpdates(t *testing.T) {
|
|||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
defer close(stopCh)
|
defer close(stopCh)
|
||||||
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, true)()
|
|
||||||
|
|
||||||
kubeClient := fake.NewSimpleClientset(resourceQuota)
|
kubeClient := fake.NewSimpleClientset(resourceQuota)
|
||||||
informerFactory := informers.NewSharedInformerFactory(kubeClient, 0)
|
informerFactory := informers.NewSharedInformerFactory(kubeClient, 0)
|
||||||
|
|
||||||
@ -458,8 +453,6 @@ func TestAdmitHandlesPVCUpdates(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, true)()
|
|
||||||
|
|
||||||
// start up quota system
|
// start up quota system
|
||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
defer close(stopCh)
|
defer close(stopCh)
|
||||||
|
@ -32,7 +32,6 @@ import (
|
|||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
storageapi "k8s.io/kubernetes/pkg/apis/storage"
|
storageapi "k8s.io/kubernetes/pkg/apis/storage"
|
||||||
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
|
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
|
||||||
"k8s.io/kubernetes/third_party/forked/gonum/graph"
|
"k8s.io/kubernetes/third_party/forked/gonum/graph"
|
||||||
"k8s.io/kubernetes/third_party/forked/gonum/graph/traverse"
|
"k8s.io/kubernetes/third_party/forked/gonum/graph/traverse"
|
||||||
@ -112,10 +111,8 @@ func (r *NodeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attribu
|
|||||||
case configMapResource:
|
case configMapResource:
|
||||||
return r.authorizeReadNamespacedObject(nodeName, configMapVertexType, attrs)
|
return r.authorizeReadNamespacedObject(nodeName, configMapVertexType, attrs)
|
||||||
case pvcResource:
|
case pvcResource:
|
||||||
if r.features.Enabled(features.ExpandPersistentVolumes) {
|
if attrs.GetSubresource() == "status" {
|
||||||
if attrs.GetSubresource() == "status" {
|
return r.authorizeStatusUpdate(nodeName, pvcVertexType, attrs)
|
||||||
return r.authorizeStatusUpdate(nodeName, pvcVertexType, attrs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return r.authorizeGet(nodeName, pvcVertexType, attrs)
|
return r.authorizeGet(nodeName, pvcVertexType, attrs)
|
||||||
case pvResource:
|
case pvResource:
|
||||||
|
@ -177,21 +177,19 @@ func buildControllerRoles() ([]rbacv1.ClusterRole, []rbacv1.ClusterRoleBinding)
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
addControllerRole(&controllerRoles, &controllerRoleBindings, rbacv1.ClusterRole{
|
||||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbacv1.ClusterRole{
|
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "expand-controller"},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "expand-controller"},
|
Rules: []rbacv1.PolicyRule{
|
||||||
Rules: []rbacv1.PolicyRule{
|
rbacv1helpers.NewRule("get", "list", "watch", "update", "patch").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get", "list", "watch", "update", "patch").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
|
rbacv1helpers.NewRule("update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie(),
|
rbacv1helpers.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
// glusterfs
|
||||||
// glusterfs
|
rbacv1helpers.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
|
rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
||||||
rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
eventsRule(),
|
||||||
eventsRule(),
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbacv1.ClusterRole{
|
addControllerRole(&controllerRoles, &controllerRoleBindings, rbacv1.ClusterRole{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "ephemeral-volume-controller"},
|
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "ephemeral-volume-controller"},
|
||||||
|
@ -24,9 +24,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
|
rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Write and other vars are slices of the allowed verbs.
|
// Write and other vars are slices of the allowed verbs.
|
||||||
@ -161,12 +159,10 @@ func NodeRules() []rbacv1.PolicyRule {
|
|||||||
rbacv1helpers.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts/token").RuleOrDie(),
|
rbacv1helpers.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts/token").RuleOrDie(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
// Use the Node authorization mode to limit a node to update status of pvc objects referenced by pods bound to itself.
|
||||||
// Use the Node authorization mode to limit a node to update status of pvc objects referenced by pods bound to itself.
|
// Use the NodeRestriction admission plugin to limit a node to just update the status stanza.
|
||||||
// Use the NodeRestriction admission plugin to limit a node to just update the status stanza.
|
pvcStatusPolicyRule := rbacv1helpers.NewRule("get", "update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie()
|
||||||
pvcStatusPolicyRule := rbacv1helpers.NewRule("get", "update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie()
|
nodePolicyRules = append(nodePolicyRules, pvcStatusPolicyRule)
|
||||||
nodePolicyRules = append(nodePolicyRules, pvcStatusPolicyRule)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CSI
|
// CSI
|
||||||
csiDriverRule := rbacv1helpers.NewRule("get", "watch", "list").Groups("storage.k8s.io").Resources("csidrivers").RuleOrDie()
|
csiDriverRule := rbacv1helpers.NewRule("get", "watch", "list").Groups("storage.k8s.io").Resources("csidrivers").RuleOrDie()
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -567,13 +567,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
// re-create a pod as an admin to add object references
|
// re-create a pod as an admin to add object references
|
||||||
expectAllowed(t, createNode2NormalPod(superuserClient))
|
expectAllowed(t, createNode2NormalPod(superuserClient))
|
||||||
|
|
||||||
// ExpandPersistentVolumes feature disabled
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, false)()
|
|
||||||
expectForbidden(t, updatePVCCapacity(node1Client))
|
|
||||||
expectForbidden(t, updatePVCCapacity(node2Client))
|
|
||||||
|
|
||||||
// ExpandPersistentVolumes feature enabled
|
// ExpandPersistentVolumes feature enabled
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, true)()
|
|
||||||
expectForbidden(t, updatePVCCapacity(node1Client))
|
expectForbidden(t, updatePVCCapacity(node1Client))
|
||||||
expectAllowed(t, updatePVCCapacity(node2Client))
|
expectAllowed(t, updatePVCCapacity(node2Client))
|
||||||
expectForbidden(t, updatePVCPhase(node2Client))
|
expectForbidden(t, updatePVCPhase(node2Client))
|
||||||
|
Loading…
Reference in New Issue
Block a user