@@ -91,6 +91,15 @@ type GCEControllerServer struct {
9191 // publish/unpublish call will clear the backoff condition for a node and
9292 // disk.
9393 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
94103}
95104
96105type csiErrorBackoffId string
@@ -272,7 +281,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
272281 var volKey * meta.Key
273282 switch params .ReplicationType {
274283 case replicationTypeNone :
275- zones , err = pickZones (ctx , gceCS , req .GetAccessibilityRequirements (), 1 , locationTopReq )
284+ zones , err = gceCS . pickZones (ctx , req .GetAccessibilityRequirements (), 1 , locationTopReq )
276285 if err != nil {
277286 return nil , status .Errorf (codes .InvalidArgument , "CreateVolume failed to pick zones for disk: %v" , err .Error ())
278287 }
@@ -282,7 +291,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
282291 volKey = meta .ZonalKey (name , zones [0 ])
283292
284293 case replicationTypeRegionalPD :
285- zones , err = pickZones (ctx , gceCS , req .GetAccessibilityRequirements (), 2 , locationTopReq )
294+ zones , err = gceCS . pickZones (ctx , req .GetAccessibilityRequirements (), 2 , locationTopReq )
286295 if err != nil {
287296 return nil , status .Errorf (codes .InvalidArgument , "CreateVolume failed to pick zones for disk: %v" , err .Error ())
288297 }
@@ -1551,7 +1560,7 @@ func prependZone(zone string, zones []string) []string {
15511560 return newZones
15521561}
15531562
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 ) {
15551564 reqZones , err := getZonesFromTopology (top .GetRequisite ())
15561565 if err != nil {
15571566 return nil , fmt .Errorf ("could not get zones from requisite topology: %w" , err )
@@ -1596,27 +1605,39 @@ func pickZonesFromTopology(top *csi.TopologyRequirement, numZones int, locationT
15961605
15971606 if numZones <= len (prefZones ) {
15981607 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 {
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+ if remainingZones .Len () < remainingNumZones {
1617+ fallbackSet := sets .NewString (fallbackRequisiteZones ... )
1618+ remainingFallbackZones := fallbackSet .Difference (prefSet )
1619+ if remainingFallbackZones .Len () >= remainingNumZones {
1620+ remainingZones = remainingFallbackZones
1621+ } else {
16101622 return nil , fmt .Errorf ("need %v zones from topology, only got %v unique zones" , numZones , reqSet .Union (prefSet ).Len ())
16111623 }
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
1616- }
1617- zones .Insert (nSlice ... )
1618- return zones .List (), nil
16191624 }
1625+
1626+ allZones := prefSet .Union (remainingZones ).List ()
1627+ sort .Strings (allZones )
1628+ var shiftIndex int
1629+ if len (prefZones ) == 0 {
1630+ // Random shift the requisite zones, since there is no preferred start.
1631+ shiftIndex = rand .Intn (len (allZones ))
1632+ } else {
1633+ shiftIndex = slices .Index (allZones , prefZones [0 ])
1634+ }
1635+ shiftedZones := append (allZones [shiftIndex :], allZones [:shiftIndex ]... )
1636+ sortedShiftedReqZones := slices .Filter (nil , shiftedZones , func (v string ) bool { return ! prefSet .Has (v ) })
1637+ zones := make ([]string , 0 , numZones )
1638+ zones = append (zones , prefZones ... )
1639+ zones = append (zones , sortedShiftedReqZones ... )
1640+ return zones [:numZones ], nil
16201641}
16211642
16221643func getZonesFromTopology (topList []* csi.Topology ) ([]string , error ) {
@@ -1652,11 +1673,11 @@ func getZoneFromSegment(seg map[string]string) (string, error) {
16521673 return zone , nil
16531674}
16541675
1655- func pickZones (ctx context.Context , gceCS * GCEControllerServer , top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements ) ([]string , error ) {
1676+ func ( gceCS * GCEControllerServer ) pickZones (ctx context.Context , top * csi.TopologyRequirement , numZones int , locationTopReq * locationRequirements ) ([]string , error ) {
16561677 var zones []string
16571678 var err error
16581679 if top != nil {
1659- zones , err = pickZonesFromTopology (top , numZones , locationTopReq )
1680+ zones , err = pickZonesFromTopology (top , numZones , locationTopReq , gceCS . fallbackRequisiteZones )
16601681 if err != nil {
16611682 return nil , fmt .Errorf ("failed to pick zones from topology: %w" , err )
16621683 }
0 commit comments