@@ -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
@@ -279,7 +288,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
279
288
var volKey * meta.Key
280
289
switch params .ReplicationType {
281
290
case replicationTypeNone :
282
- zones , err = pickZones (ctx , gceCS , req .GetAccessibilityRequirements (), 1 , locationTopReq )
291
+ zones , err = gceCS . pickZones (ctx , req .GetAccessibilityRequirements (), 1 , locationTopReq )
283
292
if err != nil {
284
293
return nil , status .Errorf (codes .InvalidArgument , "CreateVolume failed to pick zones for disk: %v" , err .Error ())
285
294
}
@@ -289,7 +298,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
289
298
volKey = meta .ZonalKey (name , zones [0 ])
290
299
291
300
case replicationTypeRegionalPD :
292
- zones , err = pickZones (ctx , gceCS , req .GetAccessibilityRequirements (), 2 , locationTopReq )
301
+ zones , err = gceCS . pickZones (ctx , req .GetAccessibilityRequirements (), 2 , locationTopReq )
293
302
if err != nil {
294
303
return nil , status .Errorf (codes .InvalidArgument , "CreateVolume failed to pick zones for disk: %v" , err .Error ())
295
304
}
@@ -1566,7 +1575,7 @@ func prependZone(zone string, zones []string) []string {
1566
1575
return newZones
1567
1576
}
1568
1577
1569
- func pickZonesFromTopology (top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements ) ([]string , error ) {
1578
+ func pickZonesFromTopology (top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements , fallbackRequisiteZones [] string ) ([]string , error ) {
1570
1579
reqZones , err := getZonesFromTopology (top .GetRequisite ())
1571
1580
if err != nil {
1572
1581
return nil , fmt .Errorf ("could not get zones from requisite topology: %w" , err )
@@ -1611,27 +1620,39 @@ func pickZonesFromTopology(top *csi.TopologyRequirement, numZones int, locationT
1611
1620
1612
1621
if numZones <= len (prefZones ) {
1613
1622
return prefZones [0 :numZones ], nil
1614
- } else {
1615
- zones := sets.String {}
1616
- // Add all preferred zones into zones
1617
- zones .Insert (prefZones ... )
1618
- remainingNumZones := numZones - len (prefZones )
1619
- // Take all of the remaining zones from requisite zones
1620
- reqSet := sets .NewString (reqZones ... )
1621
- prefSet := sets .NewString (prefZones ... )
1622
- remainingZones := reqSet .Difference (prefSet )
1623
-
1624
- if remainingZones .Len () < remainingNumZones {
1623
+ }
1624
+
1625
+ remainingNumZones := numZones - len (prefZones )
1626
+ // Take all of the remaining zones from requisite zones
1627
+ reqSet := sets .NewString (reqZones ... )
1628
+ prefSet := sets .NewString (prefZones ... )
1629
+ remainingZones := reqSet .Difference (prefSet )
1630
+
1631
+ if remainingZones .Len () < remainingNumZones {
1632
+ fallbackSet := sets .NewString (fallbackRequisiteZones ... )
1633
+ remainingFallbackZones := fallbackSet .Difference (prefSet )
1634
+ if remainingFallbackZones .Len () >= remainingNumZones {
1635
+ remainingZones = remainingFallbackZones
1636
+ } else {
1625
1637
return nil , fmt .Errorf ("need %v zones from topology, only got %v unique zones" , numZones , reqSet .Union (prefSet ).Len ())
1626
1638
}
1627
- // Add the remaining number of zones into the set
1628
- nSlice , err := pickRandAndConsecutive (remainingZones .List (), remainingNumZones )
1629
- if err != nil {
1630
- return nil , err
1631
- }
1632
- zones .Insert (nSlice ... )
1633
- return zones .List (), nil
1634
1639
}
1640
+
1641
+ allZones := prefSet .Union (remainingZones ).List ()
1642
+ sort .Strings (allZones )
1643
+ var shiftIndex int
1644
+ if len (prefZones ) == 0 {
1645
+ // Random shift the requisite zones, since there is no preferred start.
1646
+ shiftIndex = rand .Intn (len (allZones ))
1647
+ } else {
1648
+ shiftIndex = slices .Index (allZones , prefZones [0 ])
1649
+ }
1650
+ shiftedZones := append (allZones [shiftIndex :], allZones [:shiftIndex ]... )
1651
+ sortedShiftedReqZones := slices .Filter (nil , shiftedZones , func (v string ) bool { return ! prefSet .Has (v ) })
1652
+ zones := make ([]string , 0 , numZones )
1653
+ zones = append (zones , prefZones ... )
1654
+ zones = append (zones , sortedShiftedReqZones ... )
1655
+ return zones [:numZones ], nil
1635
1656
}
1636
1657
1637
1658
func getZonesFromTopology (topList []* csi.Topology ) ([]string , error ) {
@@ -1667,11 +1688,11 @@ func getZoneFromSegment(seg map[string]string) (string, error) {
1667
1688
return zone , nil
1668
1689
}
1669
1690
1670
- func pickZones (ctx context.Context , gceCS * GCEControllerServer , top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements ) ([]string , error ) {
1691
+ func ( gceCS * GCEControllerServer ) pickZones (ctx context.Context , top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements ) ([]string , error ) {
1671
1692
var zones []string
1672
1693
var err error
1673
1694
if top != nil {
1674
- zones , err = pickZonesFromTopology (top , numZones , locationTopReq )
1695
+ zones , err = pickZonesFromTopology (top , numZones , locationTopReq , gceCS . fallbackRequisiteZones )
1675
1696
if err != nil {
1676
1697
return nil , fmt .Errorf ("failed to pick zones from topology: %w" , err )
1677
1698
}
0 commit comments