diff --git a/changelogs/unreleased/4680-jxun b/changelogs/unreleased/4680-jxun new file mode 100644 index 000000000..9d6b56ba2 --- /dev/null +++ b/changelogs/unreleased/4680-jxun @@ -0,0 +1 @@ +Support regional pv for GKE \ No newline at end of file diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index a2830484a..32a9f5504 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "path/filepath" + "strings" "time" "github.com/pkg/errors" @@ -384,6 +385,7 @@ const ( awsEbsCsiZoneKey = "topology.ebs.csi.aws.com/zone" azureCsiZoneKey = "topology.disk.csi.azure.com/zone" gkeCsiZoneKey = "topology.gke.io/zone" + gkeZoneSeparator = "__" ) // takePVSnapshot triggers a snapshot for the volume/disk underlying a PersistentVolume if the provided @@ -539,15 +541,27 @@ func zoneFromPVNodeAffinity(res *corev1api.PersistentVolume, topologyKeys ...str return "", "" } keySet := sets.NewString(topologyKeys...) + providerGke := false + zones := make([]string, 0) for _, term := range nodeAffinity.Required.NodeSelectorTerms { if term.MatchExpressions == nil { continue } for _, exp := range term.MatchExpressions { if keySet.Has(exp.Key) && exp.Operator == "In" && len(exp.Values) > 0 { - return exp.Key, exp.Values[0] + if exp.Key == gkeCsiZoneKey { + providerGke = true + zones = append(zones, exp.Values[0]) + } else { + return exp.Key, exp.Values[0] + } } } } + + if providerGke { + return gkeCsiZoneKey, strings.Join(zones, gkeZoneSeparator) + } + return "", "" } diff --git a/pkg/backup/item_backupper_test.go b/pkg/backup/item_backupper_test.go index 819ca54d8..2152a5301 100644 --- a/pkg/backup/item_backupper_test.go +++ b/pkg/backup/item_backupper_test.go @@ -131,6 +131,36 @@ func Test_zoneFromPVNodeAffinity(t *testing.T) { wantKey: "topology.disk.csi.azure.com/zone", wantValue: "us-central", }, + { + /* an valid example of node affinity in a GKE's regional PV + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: topology.gke.io/zone + operator: In + values: + - us-central1-a + - matchExpressions: + - key: topology.gke.io/zone + operator: In + values: + - us-central1-c + */ + name: "Volume with multiple valid keys, and provider is gke, returns all valid entries's first zone value", + pv: builder.ForPersistentVolume("multi-matching-pv").NodeAffinityRequired( + builder.ForNodeSelector( + *builder.NewNodeSelectorTermBuilder().WithMatchExpression("topology.gke.io/zone", + "In", "us-central1-c").Result(), + *builder.NewNodeSelectorTermBuilder().WithMatchExpression("topology.gke.io/zone", + "In", "us-east-2c", "us-east-2b").Result(), + *builder.NewNodeSelectorTermBuilder().WithMatchExpression("topology.gke.io/zone", + "In", "europe-north1-a").Result(), + ).Result(), + ).Result(), + wantKey: "topology.gke.io/zone", + wantValue: "us-central1-c__us-east-2c__europe-north1-a", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {