diff --git a/service/controller.go b/service/controller.go index 1e14e331..25bfbf8c 100644 --- a/service/controller.go +++ b/service/controller.go @@ -219,11 +219,12 @@ func (s *service) CreateVolume( var volumeTopology []*csi.Topology systemSegments := map[string]string{} // topology segments matching requested system for a volume - // Handle Zone topology, which happens when node is annotated with "Zone" label + // Handle Zone topology, which happens when node is annotated with a matching zone label if len(zoneTargetMap) != 0 && accessibility != nil && len(accessibility.GetPreferred()) > 0 { for _, topo := range accessibility.GetPreferred() { for topoLabel, zoneName := range topo.Segments { - if strings.HasPrefix(topoLabel, "zone."+Name) { + Log.Infof("Zoning based on label %s", s.opts.zoneLabelKey) + if strings.HasPrefix(topoLabel, s.opts.zoneLabelKey) { zoneTarget, ok := zoneTargetMap[ZoneName(zoneName)] if !ok { Log.Infof("no zone target for %s", zoneTarget) @@ -241,7 +242,7 @@ func (s *service) CreateVolume( continue } - systemSegments["zone."+Name] = zoneName + systemSegments[s.opts.zoneLabelKey] = zoneName volumeTopology = append(volumeTopology, &csi.Topology{ Segments: systemSegments, }) diff --git a/service/features/array-config/multi_az b/service/features/array-config/multi_az index 22fe8b99..49a62da8 100644 --- a/service/features/array-config/multi_az +++ b/service/features/array-config/multi_az @@ -8,6 +8,7 @@ "systemID": "14dbbf5617523654", "zone": { "name": "zoneA", + "labelKey": "zone.csi-vxflexos.dellemc.com", "protectionDomains": [ { "name": "mocksystem", @@ -27,6 +28,7 @@ "systemID": "15dbbf5617523655", "zone": { "name": "zoneB", + "labelKey": "zone.csi-vxflexos.dellemc.com", "protectionDomains": [ { "name": "mocksystem", diff --git a/service/node.go b/service/node.go index c644d40e..b591ae33 100644 --- a/service/node.go +++ b/service/node.go @@ -758,8 +758,9 @@ func (s *service) NodeGetInfo( // csi-vxflexos.dellemc.com/: Log.Infof("Arrays: %+v", s.opts.arrays) topology := map[string]string{} - if zone, ok := labels["zone."+Name]; ok { - topology["zone."+Name] = zone + + if zone, ok := labels[s.opts.zoneLabelKey]; ok { + topology[s.opts.zoneLabelKey] = zone } for _, array := range s.opts.arrays { @@ -772,7 +773,7 @@ func (s *service) NodeGetInfo( topology[Name+"/"+array.SystemID+"-nfs"] = "true" } - if zone, ok := topology["zone."+Name]; ok { + if zone, ok := topology[s.opts.zoneLabelKey]; ok { if zone == string(array.AvailabilityZone.Name) { // Add only the secret values with the correct zone. nodeID, _ := GetNodeUID(ctx, s) diff --git a/service/service.go b/service/service.go index 9220ff03..28a9e50f 100644 --- a/service/service.go +++ b/service/service.go @@ -130,6 +130,7 @@ type ( // AvailabilityZone provides a mapping between cluster zones labels and storage systems type AvailabilityZone struct { Name ZoneName `json:"name"` + LabelKey string `json:"labelKey"` ProtectionDomains []ProtectionDomain `json:"protectionDomains"` } @@ -186,6 +187,7 @@ type Opts struct { IsQuotaEnabled bool // allow driver to enable quota limits for NFS volumes ExternalAccess string // used for adding extra IP/IP range to the NFS export KubeNodeName string + zoneLabelKey string } type service struct { @@ -417,6 +419,13 @@ func (s *service) BeforeServe( return err } + // if custom zoning is being used, find the common label from the array secret + opts.zoneLabelKey, err = getZoneKeyLabelFromSecret(opts.arrays) + if err != nil { + Log.Warnf("unable to get zone key from secret: %s", err.Error()) + return err + } + if err = s.ProcessMapSecretChange(); err != nil { Log.Warnf("unable to configure dynamic configMap secret change detection : %s", err.Error()) return err @@ -1873,3 +1882,21 @@ func ParseInt64FromContext(ctx context.Context, key string) (int64, error) { func lookupEnv(ctx context.Context, key string) (string, bool) { return csictx.LookupEnv(ctx, key) } + +func getZoneKeyLabelFromSecret(arrays map[string]*ArrayConnectionData) (string, error) { + zoneKeyLabel := "" + + for _, array := range arrays { + if array.AvailabilityZone != nil { + if zoneKeyLabel == "" { + // Assumes that the key parameter is not empty + zoneKeyLabel = array.AvailabilityZone.LabelKey + } else if zoneKeyLabel != array.AvailabilityZone.LabelKey { + Log.Warnf("array %s zone key %s does not match %s", array.SystemID, array.AvailabilityZone.LabelKey, zoneKeyLabel) + return "", fmt.Errorf("array %s zone key %s does not match %s", array.SystemID, array.AvailabilityZone.LabelKey, zoneKeyLabel) + } + } + } + + return zoneKeyLabel, nil +} diff --git a/service/service_unit_test.go b/service/service_unit_test.go index c6e8b622..9dc86453 100644 --- a/service/service_unit_test.go +++ b/service/service_unit_test.go @@ -517,6 +517,70 @@ func TestGetIPAddressByInterface(t *testing.T) { } } +func TestGetZoneKeyLabelFromSecret(t *testing.T) { + tests := []struct { + name string + arrays map[string]*ArrayConnectionData + expectedLabel string + expectedErr error + }{ + { + name: "Empty array connection data", + arrays: map[string]*ArrayConnectionData{}, + expectedLabel: "", + expectedErr: nil, + }, + { + name: "Array connection data with same zone label keys", + arrays: map[string]*ArrayConnectionData{ + "array1": { + AvailabilityZone: &AvailabilityZone{ + Name: "zone1", + LabelKey: "custom-zone.io/area", + }, + }, + "array2": { + AvailabilityZone: &AvailabilityZone{ + Name: "zone2", + LabelKey: "custom-zone.io/area", + }, + }, + }, + expectedLabel: "custom-zone.io/area", + expectedErr: nil, + }, + { + name: "Array connection data with different label keys", + arrays: map[string]*ArrayConnectionData{ + "array1": { + SystemID: "system-1", + AvailabilityZone: &AvailabilityZone{ + Name: "zone1", + LabelKey: "custom-zone-1.io/area", + }, + }, + "array2": { + SystemID: "system-2", + AvailabilityZone: &AvailabilityZone{ + Name: "zone2", + LabelKey: "custom-zone-2.io/area", + }, + }, + }, + expectedLabel: "", + expectedErr: fmt.Errorf("array system-2 zone key custom-zone-2.io/area does not match custom-zone-1.io/area"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + label, err := getZoneKeyLabelFromSecret(tt.arrays) + assert.Equal(t, err, tt.expectedErr) + assert.Equal(t, label, tt.expectedLabel) + }) + } +} + func TestFindNetworkInterfaceIPs(t *testing.T) { tests := []struct { name string diff --git a/service/step_defs_test.go b/service/step_defs_test.go index fd48055a..6e182b8d 100644 --- a/service/step_defs_test.go +++ b/service/step_defs_test.go @@ -4250,6 +4250,10 @@ func (f *feature) iUseConfig(filename string) error { } } + f.service.opts.zoneLabelKey, err = getZoneKeyLabelFromSecret(f.service.opts.arrays) + if err != nil { + return fmt.Errorf("get zone key label from secret: %s", err.Error()) + } fmt.Printf("****************************************************** s.opts.arrays %v\n", f.service.opts.arrays) f.service.systemProbeAll(context.Background()) f.adminClient = f.service.adminClients[arrayID]