@@ -21,13 +21,9 @@ import (
21
21
"sort"
22
22
23
23
"k8s.io/api/core/v1"
24
- "k8s.io/apimachinery/pkg/api/resource"
25
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26
- "k8s.io/apimachinery/pkg/labels"
27
- utilfeature "k8s.io/apiserver/pkg/util/feature"
28
24
"k8s.io/client-go/tools/cache"
29
25
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
30
- "k8s.io/kubernetes/pkg/features "
26
+ pvutil "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/util "
31
27
volumeutil "k8s.io/kubernetes/pkg/volume/util"
32
28
)
33
29
@@ -96,7 +92,7 @@ func (pvIndex *persistentVolumeOrderedIndex) findByClaim(claim *v1.PersistentVol
96
92
return nil , err
97
93
}
98
94
99
- bestVol , err := findMatchingVolume (claim , volumes , nil /* node for topology binding*/ , nil /* exclusion map */ , delayBinding )
95
+ bestVol , err := pvutil . FindMatchingVolume (claim , volumes , nil /* node for topology binding*/ , nil /* exclusion map */ , delayBinding )
100
96
if err != nil {
101
97
return nil , err
102
98
}
@@ -108,176 +104,6 @@ func (pvIndex *persistentVolumeOrderedIndex) findByClaim(claim *v1.PersistentVol
108
104
return nil , nil
109
105
}
110
106
111
- // findMatchingVolume goes through the list of volumes to find the best matching volume
112
- // for the claim.
113
- //
114
- // This function is used by both the PV controller and scheduler.
115
- //
116
- // delayBinding is true only in the PV controller path. When set, prebound PVs are still returned
117
- // as a match for the claim, but unbound PVs are skipped.
118
- //
119
- // node is set only in the scheduler path. When set, the PV node affinity is checked against
120
- // the node's labels.
121
- //
122
- // excludedVolumes is only used in the scheduler path, and is needed for evaluating multiple
123
- // unbound PVCs for a single Pod at one time. As each PVC finds a matching PV, the chosen
124
- // PV needs to be excluded from future matching.
125
- func findMatchingVolume (
126
- claim * v1.PersistentVolumeClaim ,
127
- volumes []* v1.PersistentVolume ,
128
- node * v1.Node ,
129
- excludedVolumes map [string ]* v1.PersistentVolume ,
130
- delayBinding bool ) (* v1.PersistentVolume , error ) {
131
-
132
- var smallestVolume * v1.PersistentVolume
133
- var smallestVolumeQty resource.Quantity
134
- requestedQty := claim .Spec .Resources .Requests [v1 .ResourceName (v1 .ResourceStorage )]
135
- requestedClass := v1helper .GetPersistentVolumeClaimClass (claim )
136
-
137
- var selector labels.Selector
138
- if claim .Spec .Selector != nil {
139
- internalSelector , err := metav1 .LabelSelectorAsSelector (claim .Spec .Selector )
140
- if err != nil {
141
- // should be unreachable code due to validation
142
- return nil , fmt .Errorf ("error creating internal label selector for claim: %v: %v" , claimToClaimKey (claim ), err )
143
- }
144
- selector = internalSelector
145
- }
146
-
147
- // Go through all available volumes with two goals:
148
- // - find a volume that is either pre-bound by user or dynamically
149
- // provisioned for this claim. Because of this we need to loop through
150
- // all volumes.
151
- // - find the smallest matching one if there is no volume pre-bound to
152
- // the claim.
153
- for _ , volume := range volumes {
154
- if _ , ok := excludedVolumes [volume .Name ]; ok {
155
- // Skip volumes in the excluded list
156
- continue
157
- }
158
-
159
- volumeQty := volume .Spec .Capacity [v1 .ResourceStorage ]
160
-
161
- // check if volumeModes do not match (feature gate protected)
162
- isMismatch , err := checkVolumeModeMismatches (& claim .Spec , & volume .Spec )
163
- if err != nil {
164
- return nil , fmt .Errorf ("error checking if volumeMode was a mismatch: %v" , err )
165
- }
166
- // filter out mismatching volumeModes
167
- if isMismatch {
168
- continue
169
- }
170
-
171
- // check if PV's DeletionTimeStamp is set, if so, skip this volume.
172
- if utilfeature .DefaultFeatureGate .Enabled (features .StorageObjectInUseProtection ) {
173
- if volume .ObjectMeta .DeletionTimestamp != nil {
174
- continue
175
- }
176
- }
177
-
178
- nodeAffinityValid := true
179
- if node != nil {
180
- // Scheduler path, check that the PV NodeAffinity
181
- // is satisfied by the node
182
- err := volumeutil .CheckNodeAffinity (volume , node .Labels )
183
- if err != nil {
184
- nodeAffinityValid = false
185
- }
186
- }
187
-
188
- if IsVolumeBoundToClaim (volume , claim ) {
189
- // this claim and volume are pre-bound; return
190
- // the volume if the size request is satisfied,
191
- // otherwise continue searching for a match
192
- if volumeQty .Cmp (requestedQty ) < 0 {
193
- continue
194
- }
195
-
196
- // If PV node affinity is invalid, return no match.
197
- // This means the prebound PV (and therefore PVC)
198
- // is not suitable for this node.
199
- if ! nodeAffinityValid {
200
- return nil , nil
201
- }
202
-
203
- return volume , nil
204
- }
205
-
206
- if node == nil && delayBinding {
207
- // PV controller does not bind this claim.
208
- // Scheduler will handle binding unbound volumes
209
- // Scheduler path will have node != nil
210
- continue
211
- }
212
-
213
- // filter out:
214
- // - volumes in non-available phase
215
- // - volumes bound to another claim
216
- // - volumes whose labels don't match the claim's selector, if specified
217
- // - volumes in Class that is not requested
218
- // - volumes whose NodeAffinity does not match the node
219
- if volume .Status .Phase != v1 .VolumeAvailable {
220
- // We ignore volumes in non-available phase, because volumes that
221
- // satisfies matching criteria will be updated to available, binding
222
- // them now has high chance of encountering unnecessary failures
223
- // due to API conflicts.
224
- continue
225
- } else if volume .Spec .ClaimRef != nil {
226
- continue
227
- } else if selector != nil && ! selector .Matches (labels .Set (volume .Labels )) {
228
- continue
229
- }
230
- if v1helper .GetPersistentVolumeClass (volume ) != requestedClass {
231
- continue
232
- }
233
- if ! nodeAffinityValid {
234
- continue
235
- }
236
-
237
- if node != nil {
238
- // Scheduler path
239
- // Check that the access modes match
240
- if ! checkAccessModes (claim , volume ) {
241
- continue
242
- }
243
- }
244
-
245
- if volumeQty .Cmp (requestedQty ) >= 0 {
246
- if smallestVolume == nil || smallestVolumeQty .Cmp (volumeQty ) > 0 {
247
- smallestVolume = volume
248
- smallestVolumeQty = volumeQty
249
- }
250
- }
251
- }
252
-
253
- if smallestVolume != nil {
254
- // Found a matching volume
255
- return smallestVolume , nil
256
- }
257
-
258
- return nil , nil
259
- }
260
-
261
- // checkVolumeModeMismatches is a convenience method that checks volumeMode for PersistentVolume
262
- // and PersistentVolumeClaims
263
- func checkVolumeModeMismatches (pvcSpec * v1.PersistentVolumeClaimSpec , pvSpec * v1.PersistentVolumeSpec ) (bool , error ) {
264
- if ! utilfeature .DefaultFeatureGate .Enabled (features .BlockVolume ) {
265
- return false , nil
266
- }
267
-
268
- // In HA upgrades, we cannot guarantee that the apiserver is on a version >= controller-manager.
269
- // So we default a nil volumeMode to filesystem
270
- requestedVolumeMode := v1 .PersistentVolumeFilesystem
271
- if pvcSpec .VolumeMode != nil {
272
- requestedVolumeMode = * pvcSpec .VolumeMode
273
- }
274
- pvVolumeMode := v1 .PersistentVolumeFilesystem
275
- if pvSpec .VolumeMode != nil {
276
- pvVolumeMode = * pvSpec .VolumeMode
277
- }
278
- return requestedVolumeMode != pvVolumeMode , nil
279
- }
280
-
281
107
// findBestMatchForClaim is a convenience method that finds a volume by the claim's AccessModes and requests for Storage
282
108
func (pvIndex * persistentVolumeOrderedIndex ) findBestMatchForClaim (claim * v1.PersistentVolumeClaim , delayBinding bool ) (* v1.PersistentVolume , error ) {
283
109
return pvIndex .findByClaim (claim , delayBinding )
@@ -362,19 +188,3 @@ func claimToClaimKey(claim *v1.PersistentVolumeClaim) string {
362
188
func claimrefToClaimKey (claimref * v1.ObjectReference ) string {
363
189
return fmt .Sprintf ("%s/%s" , claimref .Namespace , claimref .Name )
364
190
}
365
-
366
- // Returns true if PV satisfies all the PVC's requested AccessModes
367
- func checkAccessModes (claim * v1.PersistentVolumeClaim , volume * v1.PersistentVolume ) bool {
368
- pvModesMap := map [v1.PersistentVolumeAccessMode ]bool {}
369
- for _ , mode := range volume .Spec .AccessModes {
370
- pvModesMap [mode ] = true
371
- }
372
-
373
- for _ , mode := range claim .Spec .AccessModes {
374
- _ , ok := pvModesMap [mode ]
375
- if ! ok {
376
- return false
377
- }
378
- }
379
- return true
380
- }
0 commit comments