...
1
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
34 PluginName = "PersistentVolumeClaimResize"
35 )
36
37
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
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
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
106
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
116
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