@@ -20,7 +20,7 @@ import (
20
20
"fmt"
21
21
"hash/fnv"
22
22
"math/rand"
23
- "sort "
23
+ "slices "
24
24
"strconv"
25
25
"strings"
26
26
@@ -39,8 +39,14 @@ import (
39
39
"k8s.io/klog/v2"
40
40
)
41
41
42
+ type topologySegment struct {
43
+ Key , Value string
44
+ }
45
+
42
46
// topologyTerm represents a single term where its topology key value pairs are AND'd together.
43
- type topologyTerm map [string ]string
47
+ //
48
+ // Be sure to sort after construction for compare() and subset() to work properly.
49
+ type topologyTerm []topologySegment
44
50
45
51
func GenerateVolumeNodeAffinity (accessibleTopology []* csi.Topology ) * v1.VolumeNodeAffinity {
46
52
if len (accessibleTopology ) == 0 {
@@ -229,7 +235,8 @@ func GenerateAccessibilityRequirements(
229
235
return nil , nil
230
236
}
231
237
232
- requisiteTerms = deduplicate (requisiteTerms )
238
+ slices .SortFunc (requisiteTerms , topologyTerm .compare )
239
+ requisiteTerms = slices .CompactFunc (requisiteTerms , slices .Equal )
233
240
// TODO (verult) reduce subset duplicate terms (advanced reduction)
234
241
235
242
requirement .Requisite = toCSITopology (requisiteTerms )
@@ -243,14 +250,19 @@ func GenerateAccessibilityRequirements(
243
250
// requisiteTerms and shifting the sorted terms based on hash of pvcName and replica index suffix
244
251
hash , index := getPVCNameHashAndIndexOffset (pvcName )
245
252
i := (hash + index ) % uint32 (len (requisiteTerms ))
246
- preferredTerms = sortAndShift (requisiteTerms , nil , i )
253
+ preferredTerms = append (requisiteTerms [ i :], requisiteTerms [: i ] ... )
247
254
} else {
248
255
// Delayed binding, use topology from that node to populate preferredTerms
249
256
if strictTopology {
250
257
// In case of strict topology, preferred = requisite
251
258
preferredTerms = requisiteTerms
252
259
} else {
253
- preferredTerms = sortAndShift (requisiteTerms , selectedTopology , 0 )
260
+ for i , t := range requisiteTerms {
261
+ if t .subset (selectedTopology ) {
262
+ preferredTerms = append (requisiteTerms [i :], requisiteTerms [:i ]... )
263
+ break
264
+ }
265
+ }
254
266
if preferredTerms == nil {
255
267
// Topology from selected node is not in requisite. This case should never be hit:
256
268
// - If AllowedTopologies is specified, the scheduler should choose a node satisfying the
@@ -417,12 +429,12 @@ func flatten(allowedTopologies []v1.TopologySelectorTerm) []topologyTerm {
417
429
418
430
if len (oldTerms ) == 0 {
419
431
// No previous terms to distribute over. Simply append the new term.
420
- newTerms = append (newTerms , topologyTerm {selectorExpression .Key : v })
432
+ newTerms = append (newTerms , topologyTerm {{ selectorExpression .Key , v } })
421
433
} else {
422
434
for _ , oldTerm := range oldTerms {
423
435
// "Distribute" by adding an entry to the term
424
- newTerm := oldTerm . clone ( )
425
- newTerm [ selectorExpression .Key ] = v
436
+ newTerm := slices . Clone ( oldTerm )
437
+ newTerm = append ( newTerm , topologySegment { selectorExpression .Key , v })
426
438
newTerms = append (newTerms , newTerm )
427
439
}
428
440
}
@@ -435,41 +447,10 @@ func flatten(allowedTopologies []v1.TopologySelectorTerm) []topologyTerm {
435
447
finalTerms = append (finalTerms , oldTerms ... )
436
448
}
437
449
438
- return finalTerms
439
- }
440
-
441
- func deduplicate (terms []topologyTerm ) []topologyTerm {
442
- termMap := make (map [string ]topologyTerm )
443
- for _ , term := range terms {
444
- termMap [term .hash ()] = term
445
- }
446
-
447
- var dedupedTerms []topologyTerm
448
- for _ , term := range termMap {
449
- dedupedTerms = append (dedupedTerms , term )
450
- }
451
- return dedupedTerms
452
- }
453
-
454
- // Sort the given terms in place,
455
- // then return a new list of terms equivalent to the sorted terms, but shifted so that
456
- // either the primary term (if specified) or term at shiftIndex is the first in the list.
457
- func sortAndShift (terms []topologyTerm , primary topologyTerm , shiftIndex uint32 ) []topologyTerm {
458
- var preferredTerms []topologyTerm
459
- sort .Slice (terms , func (i , j int ) bool {
460
- return terms [i ].less (terms [j ])
461
- })
462
- if primary == nil {
463
- preferredTerms = append (terms [shiftIndex :], terms [:shiftIndex ]... )
464
- } else {
465
- for i , t := range terms {
466
- if t .subset (primary ) {
467
- preferredTerms = append (terms [i :], terms [:i ]... )
468
- break
469
- }
470
- }
450
+ for _ , term := range finalTerms {
451
+ term .sort ()
471
452
}
472
- return preferredTerms
453
+ return finalTerms
473
454
}
474
455
475
456
func getTopologyKeys (csiNode * storagev1.CSINode , driverName string ) []string {
@@ -482,14 +463,15 @@ func getTopologyKeys(csiNode *storagev1.CSINode, driverName string) []string {
482
463
}
483
464
484
465
func getTopologyFromNode (node * v1.Node , topologyKeys []string ) (term topologyTerm , isMissingKey bool ) {
485
- term = make (topologyTerm )
466
+ term = make (topologyTerm , 0 , len ( topologyKeys ) )
486
467
for _ , key := range topologyKeys {
487
468
v , ok := node .Labels [key ]
488
469
if ! ok {
489
470
return nil , true
490
471
}
491
- term [ key ] = v
472
+ term = append ( term , topologySegment { key , v })
492
473
}
474
+ term .sort ()
493
475
return term , false
494
476
}
495
477
@@ -514,56 +496,65 @@ func buildTopologyKeySelector(topologyKeys []string) (labels.Selector, error) {
514
496
return selector , nil
515
497
}
516
498
517
- func (t topologyTerm ) clone () topologyTerm {
518
- ret := make (topologyTerm )
519
- for k , v := range t {
520
- ret [k ] = v
521
- }
522
- return ret
499
+ func (t topologyTerm ) sort () {
500
+ slices .SortFunc (t , func (a , b topologySegment ) int {
501
+ r := strings .Compare (a .Key , b .Key )
502
+ if r != 0 {
503
+ return r
504
+ }
505
+ // Should not happen currently. We may support multi-value in the future?
506
+ return strings .Compare (a .Value , b .Value )
507
+ })
523
508
}
524
509
525
- // "<k1>#<v1>,<k2>#<v2>,..."
526
- // Hash properties:
527
- // - Two equivalent topologyTerms have the same hash
528
- // - Ordering of hashes correspond to a natural ordering of their topologyTerms. For example:
529
- // - com.example.csi/zone#zone1 < com.example.csi/zone#zone2
530
- // - com.example.csi/rack#zz < com.example.csi/zone#zone1
531
- // - com.example.csi/z#z1 < com.example.csi/zone#zone1
532
- // - com.example.csi/rack#rackA,com.example.csi/zone#zone2 < com.example.csi/rackB,com.example.csi/zone#zone1
533
- // Note that both '#' and ',' are less than '/', '-', '_', '.', [A-Z0-9a-z]
534
- func (t topologyTerm ) hash () string {
535
- var segments []string
536
- for k , v := range t {
537
- segments = append (segments , k + "#" + v )
510
+ func (t topologyTerm ) compare (other topologyTerm ) int {
511
+ if len (t ) != len (other ) {
512
+ return len (t ) - len (other )
538
513
}
539
-
540
- sort .Strings (segments )
541
- return strings .Join (segments , "," )
542
- }
543
-
544
- func (t topologyTerm ) less (other topologyTerm ) bool {
545
- return t .hash () < other .hash ()
514
+ for i , k1 := range t {
515
+ k2 := other [i ]
516
+ r := strings .Compare (k1 .Key , k2 .Key )
517
+ if r != 0 {
518
+ return r
519
+ }
520
+ r = strings .Compare (k1 .Value , k2 .Value )
521
+ if r != 0 {
522
+ return r
523
+ }
524
+ }
525
+ return 0
546
526
}
547
527
548
528
func (t topologyTerm ) subset (other topologyTerm ) bool {
549
- for key , tv := range t {
550
- v , ok := other [key ]
551
- if ! ok || v != tv {
529
+ if len (t ) == 0 {
530
+ return true
531
+ }
532
+ j := 0
533
+ for _ , k2 := range other {
534
+ k1 := t [j ]
535
+ if k1 .Key != k2 .Key {
536
+ continue
537
+ }
538
+ if k1 .Value != k2 .Value {
552
539
return false
553
540
}
541
+ j ++
542
+ if j == len (t ) {
543
+ // All segments in t have been checked and is present in other.
544
+ return true
545
+ }
554
546
}
555
-
556
- return true
557
- }
558
-
559
- func (t topologyTerm ) equal (other topologyTerm ) bool {
560
- return t .hash () == other .hash ()
547
+ return false
561
548
}
562
549
563
550
func toCSITopology (terms []topologyTerm ) []* csi.Topology {
564
- var out []* csi.Topology
551
+ out := make ( []* csi.Topology , 0 , len ( terms ))
565
552
for _ , term := range terms {
566
- out = append (out , & csi.Topology {Segments : term })
553
+ segs := make (map [string ]string , len (term ))
554
+ for _ , k := range term {
555
+ segs [k .Key ] = k .Value
556
+ }
557
+ out = append (out , & csi.Topology {Segments : segs })
567
558
}
568
559
return out
569
560
}
0 commit comments