1
16
17 package resize
18
19 import (
20 "context"
21 "fmt"
22 "strings"
23 "testing"
24
25 storagev1 "k8s.io/api/storage/v1"
26 "k8s.io/apimachinery/pkg/api/resource"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/runtime"
29 "k8s.io/apimachinery/pkg/runtime/schema"
30 "k8s.io/apiserver/pkg/admission"
31 "k8s.io/client-go/informers"
32 api "k8s.io/kubernetes/pkg/apis/core"
33 "k8s.io/kubernetes/pkg/controller"
34 )
35
36 func getResourceList(storage string) api.ResourceList {
37 res := api.ResourceList{}
38 if storage != "" {
39 res[api.ResourceStorage] = resource.MustParse(storage)
40 }
41 return res
42 }
43
44 func TestPVCResizeAdmission(t *testing.T) {
45 goldClassName := "gold"
46 trueVal := true
47 falseVar := false
48 goldClass := &storagev1.StorageClass{
49 TypeMeta: metav1.TypeMeta{
50 Kind: "StorageClass",
51 },
52 ObjectMeta: metav1.ObjectMeta{
53 Name: goldClassName,
54 },
55 Provisioner: "kubernetes.io/glusterfs",
56 AllowVolumeExpansion: &trueVal,
57 }
58 silverClassName := "silver"
59 silverClass := &storagev1.StorageClass{
60 TypeMeta: metav1.TypeMeta{
61 Kind: "StorageClass",
62 },
63 ObjectMeta: metav1.ObjectMeta{
64 Name: silverClassName,
65 },
66 Provisioner: "kubernetes.io/glusterfs",
67 AllowVolumeExpansion: &falseVar,
68 }
69 expectNoError := func(err error) bool {
70 return err == nil
71 }
72 expectDynamicallyProvisionedError := func(err error) bool {
73 return strings.Contains(err.Error(), "only dynamically provisioned pvc can be resized and "+
74 "the storageclass that provisions the pvc must support resize")
75 }
76 tests := []struct {
77 name string
78 resource schema.GroupVersionResource
79 subresource string
80 oldObj runtime.Object
81 newObj runtime.Object
82
83 checkError func(error) bool
84 }{
85 {
86 name: "pvc-resize, update, no error",
87 resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
88 oldObj: &api.PersistentVolumeClaim{
89 Spec: api.PersistentVolumeClaimSpec{
90 VolumeName: "volume1",
91 Resources: api.VolumeResourceRequirements{
92 Requests: getResourceList("1Gi"),
93 },
94 StorageClassName: &goldClassName,
95 },
96 Status: api.PersistentVolumeClaimStatus{
97 Capacity: getResourceList("1Gi"),
98 Phase: api.ClaimBound,
99 },
100 },
101 newObj: &api.PersistentVolumeClaim{
102 Spec: api.PersistentVolumeClaimSpec{
103 VolumeName: "volume1",
104 Resources: api.VolumeResourceRequirements{
105 Requests: getResourceList("2Gi"),
106 },
107 StorageClassName: &goldClassName,
108 },
109 Status: api.PersistentVolumeClaimStatus{
110 Capacity: getResourceList("2Gi"),
111 Phase: api.ClaimBound,
112 },
113 },
114 checkError: expectNoError,
115 },
116 {
117 name: "pvc-resize, update, dynamically provisioned error",
118 resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
119 oldObj: &api.PersistentVolumeClaim{
120 Spec: api.PersistentVolumeClaimSpec{
121 VolumeName: "volume3",
122 Resources: api.VolumeResourceRequirements{
123 Requests: getResourceList("1Gi"),
124 },
125 },
126 Status: api.PersistentVolumeClaimStatus{
127 Capacity: getResourceList("1Gi"),
128 Phase: api.ClaimBound,
129 },
130 },
131 newObj: &api.PersistentVolumeClaim{
132 Spec: api.PersistentVolumeClaimSpec{
133 VolumeName: "volume3",
134 Resources: api.VolumeResourceRequirements{
135 Requests: getResourceList("2Gi"),
136 },
137 },
138 Status: api.PersistentVolumeClaimStatus{
139 Capacity: getResourceList("2Gi"),
140 Phase: api.ClaimBound,
141 },
142 },
143 checkError: expectDynamicallyProvisionedError,
144 },
145 {
146 name: "pvc-resize, update, dynamically provisioned error",
147 resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
148 oldObj: &api.PersistentVolumeClaim{
149 Spec: api.PersistentVolumeClaimSpec{
150 VolumeName: "volume4",
151 Resources: api.VolumeResourceRequirements{
152 Requests: getResourceList("1Gi"),
153 },
154 StorageClassName: &silverClassName,
155 },
156 Status: api.PersistentVolumeClaimStatus{
157 Capacity: getResourceList("1Gi"),
158 Phase: api.ClaimBound,
159 },
160 },
161 newObj: &api.PersistentVolumeClaim{
162 Spec: api.PersistentVolumeClaimSpec{
163 VolumeName: "volume4",
164 Resources: api.VolumeResourceRequirements{
165 Requests: getResourceList("2Gi"),
166 },
167 StorageClassName: &silverClassName,
168 },
169 Status: api.PersistentVolumeClaimStatus{
170 Capacity: getResourceList("2Gi"),
171 Phase: api.ClaimBound,
172 },
173 },
174 checkError: expectDynamicallyProvisionedError,
175 },
176 {
177 name: "PVC update with no change in size",
178 resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
179 oldObj: &api.PersistentVolumeClaim{
180 Spec: api.PersistentVolumeClaimSpec{
181 Resources: api.VolumeResourceRequirements{
182 Requests: getResourceList("1Gi"),
183 },
184 StorageClassName: &silverClassName,
185 },
186 Status: api.PersistentVolumeClaimStatus{
187 Capacity: getResourceList("0Gi"),
188 Phase: api.ClaimPending,
189 },
190 },
191 newObj: &api.PersistentVolumeClaim{
192 Spec: api.PersistentVolumeClaimSpec{
193 VolumeName: "volume4",
194 Resources: api.VolumeResourceRequirements{
195 Requests: getResourceList("1Gi"),
196 },
197 StorageClassName: &silverClassName,
198 },
199 Status: api.PersistentVolumeClaimStatus{
200 Capacity: getResourceList("1Gi"),
201 Phase: api.ClaimBound,
202 },
203 },
204 checkError: expectNoError,
205 },
206 {
207 name: "expand pvc in pending state",
208 resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
209 oldObj: &api.PersistentVolumeClaim{
210 Spec: api.PersistentVolumeClaimSpec{
211 Resources: api.VolumeResourceRequirements{
212 Requests: getResourceList("1Gi"),
213 },
214 StorageClassName: &silverClassName,
215 },
216 Status: api.PersistentVolumeClaimStatus{
217 Capacity: getResourceList("0Gi"),
218 Phase: api.ClaimPending,
219 },
220 },
221 newObj: &api.PersistentVolumeClaim{
222 Spec: api.PersistentVolumeClaimSpec{
223 Resources: api.VolumeResourceRequirements{
224 Requests: getResourceList("2Gi"),
225 },
226 StorageClassName: &silverClassName,
227 },
228 Status: api.PersistentVolumeClaimStatus{
229 Capacity: getResourceList("0Gi"),
230 Phase: api.ClaimPending,
231 },
232 },
233 checkError: func(err error) bool {
234 return strings.Contains(err.Error(), "Only bound persistent volume claims can be expanded")
235 },
236 },
237 }
238
239 ctrl := newPlugin()
240 informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
241 ctrl.SetExternalKubeInformerFactory(informerFactory)
242 err := ctrl.ValidateInitialization()
243 if err != nil {
244 t.Fatalf("neither pv lister nor storageclass lister can be nil")
245 }
246
247 scs := []*storagev1.StorageClass{}
248 scs = append(scs, goldClass, silverClass)
249 for _, sc := range scs {
250 err := informerFactory.Storage().V1().StorageClasses().Informer().GetStore().Add(sc)
251 if err != nil {
252 fmt.Println("add storageclass error: ", err)
253 }
254 }
255
256 for _, tc := range tests {
257 operation := admission.Update
258 operationOptions := &metav1.CreateOptions{}
259 attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, operationOptions, false, nil)
260
261 err := ctrl.Validate(context.TODO(), attributes, nil)
262 if !tc.checkError(err) {
263 t.Errorf("%v: unexpected err: %v", tc.name, err)
264 }
265 }
266
267 }
268
View as plain text