...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/resize/admission.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/resize

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

View as plain text