@@ -91,6 +91,15 @@ type GCEControllerServer struct {
91
91
// publish/unpublish call will clear the backoff condition for a node and
92
92
// disk.
93
93
errorBackoff * csiErrorBackoff
94
+
95
+ // Requisite zones to fallback to when provisioning a disk.
96
+ // If there are an insufficient number of zones available in the union
97
+ // of preferred/requisite topology, this list is used instead of
98
+ // the passed in requisite topology.
99
+ // The main use case of this field is to support Regional Persistent Disk
100
+ // provisioning in GKE Autopilot, where a GKE cluster to
101
+ // be scaled down to 1 zone.
102
+ fallbackRequisiteZones []string
94
103
}
95
104
96
105
type csiErrorBackoffId string
@@ -272,7 +281,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
272
281
var volKey * meta.Key
273
282
switch params .ReplicationType {
274
283
case replicationTypeNone :
275
- zones , err = pickZones (ctx , gceCS , req .GetAccessibilityRequirements (), 1 , locationTopReq )
284
+ zones , err = gceCS . pickZones (ctx , req .GetAccessibilityRequirements (), 1 , locationTopReq )
276
285
if err != nil {
277
286
return nil , status .Errorf (codes .InvalidArgument , "CreateVolume failed to pick zones for disk: %v" , err .Error ())
278
287
}
@@ -282,7 +291,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
282
291
volKey = meta .ZonalKey (name , zones [0 ])
283
292
284
293
case replicationTypeRegionalPD :
285
- zones , err = pickZones (ctx , gceCS , req .GetAccessibilityRequirements (), 2 , locationTopReq )
294
+ zones , err = gceCS . pickZones (ctx , req .GetAccessibilityRequirements (), 2 , locationTopReq )
286
295
if err != nil {
287
296
return nil , status .Errorf (codes .InvalidArgument , "CreateVolume failed to pick zones for disk: %v" , err .Error ())
288
297
}
@@ -1551,7 +1560,7 @@ func prependZone(zone string, zones []string) []string {
1551
1560
return newZones
1552
1561
}
1553
1562
1554
- func pickZonesFromTopology (top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements ) ([]string , error ) {
1563
+ func pickZonesFromTopology (top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements , fallbackRequisiteZones [] string ) ([]string , error ) {
1555
1564
reqZones , err := getZonesFromTopology (top .GetRequisite ())
1556
1565
if err != nil {
1557
1566
return nil , fmt .Errorf ("could not get zones from requisite topology: %w" , err )
@@ -1596,27 +1605,37 @@ func pickZonesFromTopology(top *csi.TopologyRequirement, numZones int, locationT
1596
1605
1597
1606
if numZones <= len (prefZones ) {
1598
1607
return prefZones [0 :numZones ], nil
1599
- } else {
1600
- zones := sets.String {}
1601
- // Add all preferred zones into zones
1602
- zones .Insert (prefZones ... )
1603
- remainingNumZones := numZones - len (prefZones )
1604
- // Take all of the remaining zones from requisite zones
1605
- reqSet := sets .NewString (reqZones ... )
1606
- prefSet := sets .NewString (prefZones ... )
1607
- remainingZones := reqSet .Difference (prefSet )
1608
-
1609
- if remainingZones .Len () < remainingNumZones {
1610
- return nil , fmt .Errorf ("need %v zones from topology, only got %v unique zones" , numZones , reqSet .Union (prefSet ).Len ())
1611
- }
1612
- // Add the remaining number of zones into the set
1613
- nSlice , err := pickRandAndConsecutive (remainingZones .List (), remainingNumZones )
1614
- if err != nil {
1615
- return nil , err
1608
+ }
1609
+
1610
+ remainingNumZones := numZones - len (prefZones )
1611
+ // Take all of the remaining zones from requisite zones
1612
+ reqSet := sets .NewString (reqZones ... )
1613
+ prefSet := sets .NewString (prefZones ... )
1614
+ remainingZones := reqSet .Difference (prefSet )
1615
+
1616
+ fallbackSet := sets .NewString (fallbackRequisiteZones ... )
1617
+ remainingFallbackZones := fallbackSet .Difference (prefSet )
1618
+ if remainingZones .Len () < remainingNumZones {
1619
+ if remainingFallbackZones .Len () >= remainingNumZones {
1620
+ remainingZones = remainingFallbackZones
1621
+ } else {
1622
+ return nil , fmt .Errorf ("need %v zones from topology, only got %v unique zones" , numZones , remainingZones .Union (prefSet ).Len ())
1616
1623
}
1617
- zones .Insert (nSlice ... )
1618
- return zones .List (), nil
1619
1624
}
1625
+
1626
+ allZones := prefSet .Union (remainingZones ).List ()
1627
+ sort .Strings (allZones )
1628
+ var shiftIndex int
1629
+ if len (prefZones ) == 0 {
1630
+ shiftIndex = rand .Intn (len (allZones ))
1631
+ } else {
1632
+ shiftIndex = slices .Index (allZones , prefZones [0 ])
1633
+ }
1634
+ shiftedZones := append (allZones [shiftIndex :], allZones [:shiftIndex ]... )
1635
+ zones := make ([]string , 0 , numZones )
1636
+ zones = append (zones , prefZones ... )
1637
+ zones = slices .Filter (zones , shiftedZones , func (v string ) bool { return ! prefSet .Has (v ) })
1638
+ return zones [:numZones ], nil
1620
1639
}
1621
1640
1622
1641
func getZonesFromTopology (topList []* csi.Topology ) ([]string , error ) {
@@ -1652,11 +1671,11 @@ func getZoneFromSegment(seg map[string]string) (string, error) {
1652
1671
return zone , nil
1653
1672
}
1654
1673
1655
- func pickZones (ctx context.Context , gceCS * GCEControllerServer , top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements ) ([]string , error ) {
1674
+ func ( gceCS * GCEControllerServer ) pickZones (ctx context.Context , top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements ) ([]string , error ) {
1656
1675
var zones []string
1657
1676
var err error
1658
1677
if top != nil {
1659
- zones , err = pickZonesFromTopology (top , numZones , locationTopReq )
1678
+ zones , err = pickZonesFromTopology (top , numZones , locationTopReq , gceCS . fallbackRequisiteZones )
1660
1679
if err != nil {
1661
1680
return nil , fmt .Errorf ("failed to pick zones from topology: %w" , err )
1662
1681
}
0 commit comments