k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/admission/storage/persistentvolume/resize/admission.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package resize 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 24 "k8s.io/apiserver/pkg/admission" 25 genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" 26 "k8s.io/client-go/informers" 27 storagev1listers "k8s.io/client-go/listers/storage/v1" 28 api "k8s.io/kubernetes/pkg/apis/core" 29 apihelper "k8s.io/kubernetes/pkg/apis/core/helper" 30 ) 31 32 const ( 33 // PluginName is the name of pvc resize admission plugin 34 PluginName = "PersistentVolumeClaimResize" 35 ) 36 37 // Register registers a plugin 38 func Register(plugins *admission.Plugins) { 39 plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { 40 plugin := newPlugin() 41 return plugin, nil 42 }) 43 } 44 45 var _ admission.Interface = &persistentVolumeClaimResize{} 46 var _ admission.ValidationInterface = &persistentVolumeClaimResize{} 47 var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&persistentVolumeClaimResize{}) 48 49 type persistentVolumeClaimResize struct { 50 *admission.Handler 51 52 scLister storagev1listers.StorageClassLister 53 } 54 55 func newPlugin() *persistentVolumeClaimResize { 56 return &persistentVolumeClaimResize{ 57 Handler: admission.NewHandler(admission.Update), 58 } 59 } 60 61 func (pvcr *persistentVolumeClaimResize) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { 62 scInformer := f.Storage().V1().StorageClasses() 63 pvcr.scLister = scInformer.Lister() 64 pvcr.SetReadyFunc(scInformer.Informer().HasSynced) 65 } 66 67 // ValidateInitialization ensures lister is set. 68 func (pvcr *persistentVolumeClaimResize) ValidateInitialization() error { 69 if pvcr.scLister == nil { 70 return fmt.Errorf("missing storageclass lister") 71 } 72 return nil 73 } 74 75 func (pvcr *persistentVolumeClaimResize) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error { 76 if a.GetResource().GroupResource() != api.Resource("persistentvolumeclaims") { 77 return nil 78 } 79 80 if len(a.GetSubresource()) != 0 { 81 return nil 82 } 83 84 pvc, ok := a.GetObject().(*api.PersistentVolumeClaim) 85 // if we can't convert then we don't handle this object so just return 86 if !ok { 87 return nil 88 } 89 oldPvc, ok := a.GetOldObject().(*api.PersistentVolumeClaim) 90 if !ok { 91 return nil 92 } 93 94 oldSize := oldPvc.Spec.Resources.Requests[api.ResourceStorage] 95 newSize := pvc.Spec.Resources.Requests[api.ResourceStorage] 96 97 if newSize.Cmp(oldSize) <= 0 { 98 return nil 99 } 100 101 if oldPvc.Status.Phase != api.ClaimBound { 102 return admission.NewForbidden(a, fmt.Errorf("Only bound persistent volume claims can be expanded")) 103 } 104 105 // Growing Persistent volumes is only allowed for PVCs for which their StorageClass 106 // explicitly allows it 107 if !pvcr.allowResize(pvc, oldPvc) { 108 return admission.NewForbidden(a, fmt.Errorf("only dynamically provisioned pvc can be resized and "+ 109 "the storageclass that provisions the pvc must support resize")) 110 } 111 112 return nil 113 } 114 115 // Growing Persistent volumes is only allowed for PVCs for which their StorageClass 116 // explicitly allows it. 117 func (pvcr *persistentVolumeClaimResize) allowResize(pvc, oldPvc *api.PersistentVolumeClaim) bool { 118 pvcStorageClass := apihelper.GetPersistentVolumeClaimClass(pvc) 119 oldPvcStorageClass := apihelper.GetPersistentVolumeClaimClass(oldPvc) 120 if pvcStorageClass == "" || oldPvcStorageClass == "" || pvcStorageClass != oldPvcStorageClass { 121 return false 122 } 123 sc, err := pvcr.scLister.Get(pvcStorageClass) 124 if err != nil { 125 return false 126 } 127 if sc.AllowVolumeExpansion != nil { 128 return *sc.AllowVolumeExpansion 129 } 130 return false 131 }