From fc70ca93f8f7eebaddc3d4a51ada4ef89dce93d6 Mon Sep 17 00:00:00 2001 From: lukeatdell <115811384+lukeatdell@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:16:35 -0600 Subject: [PATCH] Driver Node should only ping arrays within the zone on which the pod is scheduled (#378) * configure driver node service to only ping arrays in the same zone as the service. Co-authored-by: Fernando Alfaro Campos Co-authored-by: Trevor Dawe --- go.mod | 3 +- go.sum | 14 +- service/controller.go | 94 ++++++++-- service/controller_test.go | 266 ++++++++++++++++++++++++++++ service/features/service.feature | 42 +++-- service/identity.go | 10 +- service/node.go | 5 + service/service.go | 64 ++++++- service/service_test.go | 222 ++++++++++++++++++++++- service/step_defs_test.go | 292 ++++++++++++++++++------------- 10 files changed, 858 insertions(+), 154 deletions(-) create mode 100644 service/controller_test.go diff --git a/go.mod b/go.mod index bfc92582..a42cc231 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ module github.com/dell/csi-vxflexos/v2 go 1.23 require ( - github.com/akutz/memconn v0.1.0 github.com/apparentlymart/go-cidr v1.1.0 github.com/container-storage-interface/spec v1.6.0 github.com/cucumber/godog v0.15.0 @@ -16,7 +15,7 @@ require ( github.com/dell/dell-csi-extensions/volumeGroupSnapshot v1.7.0 github.com/dell/gocsi v1.12.0 github.com/dell/gofsutil v1.17.0 - github.com/dell/goscaleio v1.17.1 + github.com/dell/goscaleio v1.17.2-0.20241218182509-936b677c46d5 github.com/fsnotify/fsnotify v1.8.0 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 diff --git a/go.sum b/go.sum index ad8d59db..b7c71831 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,6 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/akutz/gosync v0.1.0 h1:naxPT/aDYDh79PMwM3XmencmNQeYmpNFSZy4ZE9zIW0= github.com/akutz/gosync v0.1.0/go.mod h1:I8I4aiqJI1nqaeYOOB1WS+CgRJVVPqhct9Y4njywM84= -github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= -github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -108,8 +106,16 @@ github.com/dell/gocsi v1.12.0 h1:Dn/8f2BLovo57T/aC5pP/4Eqz4h6WX8SbX+hxT5NlvQ= github.com/dell/gocsi v1.12.0/go.mod h1:hJURrmDrXDGW4xVtgi5Kx6zUsU3ht9l+nlreNx33rf0= github.com/dell/gofsutil v1.17.0 h1:QA6gUb1mz8kXNEN4eEx47OHCz8nSqZrrCnaDUYmV5EY= github.com/dell/gofsutil v1.17.0/go.mod h1:PN2hWl/pVLQiTsFR0X1x+GfhfOrfW8pGgH5xGcGMeFs= -github.com/dell/goscaleio v1.17.1 h1:0gwR1c55ij3xVu/ARDWQNxBKCRlxMmg61n+5gKBX3v8= -github.com/dell/goscaleio v1.17.1/go.mod h1:7bX3rL8JWMmdifGr/UeD/Ju9wbkHUqvKDrbdu7XyGm8= +github.com/dell/goscaleio v1.17.2-0.20241209165307-dcbadc33ab2e h1:Y+F8YP3ceH6XRz0phFV5VpS2Pmoi8f0Vtg261/C2pZo= +github.com/dell/goscaleio v1.17.2-0.20241209165307-dcbadc33ab2e/go.mod h1:7bX3rL8JWMmdifGr/UeD/Ju9wbkHUqvKDrbdu7XyGm8= +github.com/dell/goscaleio v1.17.2-0.20241213145027-141cfe292cfa h1:9honWWT9xEcI0OWyLtiWIDCaMAEpBAeyyzW+KPRVh10= +github.com/dell/goscaleio v1.17.2-0.20241213145027-141cfe292cfa/go.mod h1:7bX3rL8JWMmdifGr/UeD/Ju9wbkHUqvKDrbdu7XyGm8= +github.com/dell/goscaleio v1.17.2-0.20241213204026-19006b56eb26 h1:Kg6MSwBmAlmUDWRKiG0YJRv1xd8qi9+mW7vAVNnghj4= +github.com/dell/goscaleio v1.17.2-0.20241213204026-19006b56eb26/go.mod h1:7bX3rL8JWMmdifGr/UeD/Ju9wbkHUqvKDrbdu7XyGm8= +github.com/dell/goscaleio v1.17.2-0.20241213215244-2164caaef4ab h1:DYWY7fs8v1VbmzF2pAx7peZtKhkppW5NCIyHCJN1fS4= +github.com/dell/goscaleio v1.17.2-0.20241213215244-2164caaef4ab/go.mod h1:7bX3rL8JWMmdifGr/UeD/Ju9wbkHUqvKDrbdu7XyGm8= +github.com/dell/goscaleio v1.17.2-0.20241218182509-936b677c46d5 h1:d7DwHvp7/hESR742f4iurtH3nHHSGPvnMadujZA2hsU= +github.com/dell/goscaleio v1.17.2-0.20241218182509-936b677c46d5/go.mod h1:2BsR92dYYnSmbZ34ixYdsucfyoQBDlbhbUUKnv6WalQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= diff --git a/service/controller.go b/service/controller.go index 83a66219..d87c7e02 100644 --- a/service/controller.go +++ b/service/controller.go @@ -2270,13 +2270,16 @@ func (s *service) getSystemCapacity(ctx context.Context, systemID, protectionDom adminClient := s.adminClients[systemID] system := s.systems[systemID] + if adminClient == nil || system == nil { + return 0, fmt.Errorf("can't find adminClient or system by id %s", systemID) + } var statsFunc func() (*siotypes.Statistics, error) // Default to get Capacity of system statsFunc = system.GetStatistics - if len(spName) > 0 { + if len(spName) > 0 && spName[0] != "" { // if storage pool is given, get capacity of storage pool pdID, err := s.getProtectionDomainIDFromName(systemID, protectionDomain) if err != nil { @@ -2369,6 +2372,15 @@ func (s *service) GetCapacity( } } + // If using availability zones, get capacity for the system in the zone + // using accessible topology parameter from k8s. + if s.opts.zoneLabelKey != "" { + systemID, err = s.getSystemIDFromZoneLabelKey(req) + if err != nil { + return nil, status.Errorf(codes.Internal, "%s", err.Error()) + } + } + if systemID == "" { // Get capacity of storage pool spname in all systems, return total capacity capacity, err = s.getCapacityForAllSystems(ctx, "", spname) @@ -2411,6 +2423,28 @@ func (s *service) GetCapacity( }, nil } +// getSystemIDFromZoneLabelKey returns the system ID associated with the zoneLabelKey if zoneLabelKey is set and +// contains an associated zone name. Returns an empty string otherwise. +func (s *service) getSystemIDFromZoneLabelKey(req *csi.GetCapacityRequest) (systemID string, err error) { + zoneName, ok := req.AccessibleTopology.Segments[s.opts.zoneLabelKey] + if !ok { + Log.Infof("could not get availability zone from accessible topology. Getting capacity for all systems") + return "", nil + } + + // find the systemID with the matching zone name + for _, array := range s.opts.arrays { + if zoneName == string(array.AvailabilityZone.Name) { + systemID = array.SystemID + break + } + } + if systemID == "" { + return "", fmt.Errorf("could not find an array assigned to zone '%s'", zoneName) + } + return systemID, nil +} + func (s *service) getMaximumVolumeSize(systemID string) (int64, error) { valueInCache, found := getCachedMaximumVolumeSize(systemID) if !found || valueInCache < 0 { @@ -2558,15 +2592,51 @@ func (s *service) ControllerGetCapabilities( }, nil } +func (s *service) getZoneFromZoneLabelKey(ctx context.Context, zoneLabelKey string) (zone string, err error) { + // get labels for this service, s + labels, err := GetNodeLabels(ctx, s) + if err != nil { + return "", err + } + + Log.Infof("Listing labels: %v", labels) + + // get the zone name from the labels + if val, ok := labels[zoneLabelKey]; ok { + return val, nil + } + + return "", fmt.Errorf("label %s not found", zoneLabelKey) +} + // systemProbeAll will iterate through all arrays in service.opts.arrays and probe them. If failed, it logs // the failed system name func (s *service) systemProbeAll(ctx context.Context) error { // probe all arrays - Log.Infof("Probing all arrays. Number of arrays: %d", len(s.opts.arrays)) + Log.Infoln("Probing all associated arrays") allArrayFail := true errMap := make(map[string]error) + zoneName := "" + usingZones := s.opts.zoneLabelKey != "" && s.isNodeMode() + + if usingZones { + var err error + zoneName, err = s.getZoneFromZoneLabelKey(ctx, s.opts.zoneLabelKey) + if err != nil { + return err + } + Log.Infof("probing zoneLabel '%s', zone value: '%s'", s.opts.zoneLabelKey, zoneName) + } for _, array := range s.opts.arrays { + // If zone information is available, use it to probe the array + if usingZones && !array.isInZone(zoneName) { + // Driver node containers should not probe arrays that exist outside their assigned zone + // Driver controller container should probe all arrays + Log.Infof("array %s zone %s does not match %s, not pinging this array\n", array.SystemID, array.AvailabilityZone.Name, zoneName) + continue + } + err := s.systemProbe(ctx, array) systemID := array.SystemID if err == nil { @@ -2587,23 +2657,23 @@ func (s *service) systemProbeAll(ctx context.Context) error { } // systemProbe will probe the given array -func (s *service) systemProbe(_ context.Context, array *ArrayConnectionData) error { +func (s *service) systemProbe(ctx context.Context, array *ArrayConnectionData) error { // Check that we have the details needed to login to the Gateway if array.Endpoint == "" { return status.Error(codes.FailedPrecondition, - "missing VxFlexOS Gateway endpoint") + "missing PowerFlex Gateway endpoint") } if array.Username == "" { return status.Error(codes.FailedPrecondition, - "missing VxFlexOS MDM user") + "missing PowerFlex MDM user") } if array.Password == "" { return status.Error(codes.FailedPrecondition, - "missing VxFlexOS MDM password") + "missing PowerFlex MDM password") } if array.SystemID == "" { return status.Error(codes.FailedPrecondition, - "missing VxFlexOS system name") + "missing PowerFlex system name") } var altSystemNames []string if array.AllSystemNames != "" { @@ -2626,25 +2696,27 @@ func (s *service) systemProbe(_ context.Context, array *ArrayConnectionData) err } } + Log.Printf("Login to PowerFlex Gateway, system=%s, endpoint=%s, user=%s\n", systemID, array.Endpoint, array.Username) + if s.adminClients[systemID].GetToken() == "" { - _, err := s.adminClients[systemID].Authenticate(&goscaleio.ConfigConnect{ + _, err := s.adminClients[systemID].WithContext(ctx).Authenticate(&goscaleio.ConfigConnect{ Endpoint: array.Endpoint, Username: array.Username, Password: array.Password, }) if err != nil { return status.Errorf(codes.FailedPrecondition, - "unable to login to VxFlexOS Gateway: %s", err.Error()) + "unable to login to PowerFlex Gateway: %s", err.Error()) } } // initialize system if needed if s.systems[systemID] == nil { - system, err := s.adminClients[systemID].FindSystem( + system, err := s.adminClients[systemID].WithContext(ctx).FindSystem( array.SystemID, array.SystemID, "") if err != nil { return status.Errorf(codes.FailedPrecondition, - "unable to find matching VxFlexOS system name: %s", + "unable to find matching PowerFlex system name: %s", err.Error()) } s.systems[systemID] = system diff --git a/service/controller_test.go b/service/controller_test.go new file mode 100644 index 00000000..3fa6f13d --- /dev/null +++ b/service/controller_test.go @@ -0,0 +1,266 @@ +// Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package service + +import ( + "errors" + "sync" + "testing" + + csi "github.com/container-storage-interface/spec/lib/go/csi" + sio "github.com/dell/goscaleio" + siotypes "github.com/dell/goscaleio/types/v1" + "golang.org/x/net/context" +) + +func Test_service_getZoneFromZoneLabelKey(t *testing.T) { + type fields struct { + opts Opts + adminClients map[string]*sio.Client + systems map[string]*sio.System + mode string + volCache []*siotypes.Volume + volCacheSystemID string + snapCache []*siotypes.Volume + snapCacheSystemID string + privDir string + storagePoolIDToName map[string]string + statisticsCounter int + volumePrefixToSystems map[string][]string + connectedSystemNameToID map[string]string + } + + type args struct { + ctx context.Context + zoneLabelKey string + } + + const validTopologyKey = "topology.kubernetes.io/zone" + const validZone = "zoneA" + + tests := map[string]struct { + fields fields + args args + wantZone string + wantErr bool + getNodeLabelFunc func(ctx context.Context, s *service) (map[string]string, error) + }{ + "success": { + // happy path test + args: args{ + ctx: context.Background(), + zoneLabelKey: validTopologyKey, + }, + wantZone: "zoneA", + wantErr: false, + getNodeLabelFunc: func(_ context.Context, _ *service) (map[string]string, error) { + nodeLabels := map[string]string{validTopologyKey: validZone} + return nodeLabels, nil + }, + }, + "use bad zone label key": { + // The key args.zoneLabelKey will not be found in the map returned by getNodeLabelFunc + args: args{ + ctx: context.Background(), + zoneLabelKey: "badkey", + }, + wantZone: "", + wantErr: true, + getNodeLabelFunc: func(_ context.Context, _ *service) (map[string]string, error) { + return nil, nil + }, + }, + "fail to get node labels": { + // getNodeLabelFunc will return an error, triggering failure to get the labels + args: args{ + ctx: context.Background(), + zoneLabelKey: "unimportant", + }, + wantZone: "", + wantErr: true, + getNodeLabelFunc: func(_ context.Context, _ *service) (map[string]string, error) { + return nil, errors.New("") + }, + }, + } + for testName, tt := range tests { + t.Run(testName, func(t *testing.T) { + s := &service{ + opts: tt.fields.opts, + adminClients: tt.fields.adminClients, + systems: tt.fields.systems, + mode: tt.fields.mode, + volCache: tt.fields.volCache, + volCacheRWL: sync.RWMutex{}, + volCacheSystemID: tt.fields.volCacheSystemID, + snapCache: tt.fields.snapCache, + snapCacheRWL: sync.RWMutex{}, + snapCacheSystemID: tt.fields.snapCacheSystemID, + privDir: tt.fields.privDir, + storagePoolIDToName: tt.fields.storagePoolIDToName, + statisticsCounter: tt.fields.statisticsCounter, + volumePrefixToSystems: tt.fields.volumePrefixToSystems, + connectedSystemNameToID: tt.fields.connectedSystemNameToID, + } + GetNodeLabels = tt.getNodeLabelFunc + gotZone, err := s.getZoneFromZoneLabelKey(tt.args.ctx, tt.args.zoneLabelKey) + if (err != nil) != tt.wantErr { + t.Errorf("service.getZoneFromZoneLabelKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotZone != tt.wantZone { + t.Errorf("service.getZoneFromZoneLabelKey() = %v, want %v", gotZone, tt.wantZone) + } + }) + } +} + +func Test_service_getSystemIDFromZoneLabelKey(t *testing.T) { + type fields struct { + opts Opts + adminClients map[string]*sio.Client + systems map[string]*sio.System + mode string + volCache []*siotypes.Volume + volCacheSystemID string + snapCache []*siotypes.Volume + snapCacheSystemID string + privDir string + storagePoolIDToName map[string]string + statisticsCounter int + volumePrefixToSystems map[string][]string + connectedSystemNameToID map[string]string + } + + type args struct { + req *csi.GetCapacityRequest + } + + const validSystemID = "valid-id" + const validTopologyKey = "topology.kubernetes.io/zone" + const validZone = "zoneA" + + tests := map[string]struct { + fields fields + args args + wantSystemID string + wantErr bool + }{ + "success": { + // happy path test + wantErr: false, + wantSystemID: validSystemID, + args: args{ + req: &csi.GetCapacityRequest{ + AccessibleTopology: &csi.Topology{ + Segments: map[string]string{ + validTopologyKey: validZone, + }, + }, + }, + }, + fields: fields{ + opts: Opts{ + zoneLabelKey: validTopologyKey, + arrays: map[string]*ArrayConnectionData{ + "array1": { + SystemID: validSystemID, + AvailabilityZone: &AvailabilityZone{ + Name: validZone, + }, + }, + }, + }, + }, + }, + "topology not passed with csi request": { + // should return an empty string if no topology info is passed + // with the csi request + wantErr: false, + wantSystemID: "", + args: args{ + req: &csi.GetCapacityRequest{ + AccessibleTopology: &csi.Topology{ + // don't pass any topology info with the request + Segments: map[string]string{}, + }, + }, + }, + fields: fields{ + opts: Opts{ + zoneLabelKey: validTopologyKey, + }, + }, + }, + "zone name missing in secret": { + // topology information in the csi request does not match + // any of the arrays in the secret + wantErr: true, + wantSystemID: "", + args: args{ + req: &csi.GetCapacityRequest{ + AccessibleTopology: &csi.Topology{ + Segments: map[string]string{ + validTopologyKey: validZone, + }, + }, + }, + }, + fields: fields{ + opts: Opts{ + zoneLabelKey: validTopologyKey, + arrays: map[string]*ArrayConnectionData{ + "array1": { + SystemID: validSystemID, + AvailabilityZone: &AvailabilityZone{ + // ensure the zone name will not match the topology key value + // in the request + Name: validZone + "no-match", + }, + }, + }, + }, + }, + }, + } + for testName, tt := range tests { + t.Run(testName, func(t *testing.T) { + s := &service{ + opts: tt.fields.opts, + adminClients: tt.fields.adminClients, + systems: tt.fields.systems, + mode: tt.fields.mode, + volCache: tt.fields.volCache, + volCacheRWL: sync.RWMutex{}, + volCacheSystemID: tt.fields.volCacheSystemID, + snapCache: tt.fields.snapCache, + snapCacheRWL: sync.RWMutex{}, + snapCacheSystemID: tt.fields.snapCacheSystemID, + privDir: tt.fields.privDir, + storagePoolIDToName: tt.fields.storagePoolIDToName, + statisticsCounter: tt.fields.statisticsCounter, + volumePrefixToSystems: tt.fields.volumePrefixToSystems, + connectedSystemNameToID: tt.fields.connectedSystemNameToID, + } + gotSystemID, err := s.getSystemIDFromZoneLabelKey(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("service.getSystemIDFromZoneLabelKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotSystemID != tt.wantSystemID { + t.Errorf("service.getSystemIDFromZoneLabelKey() = %v, want %v", gotSystemID, tt.wantSystemID) + } + }) + } +} diff --git a/service/features/service.feature b/service/features/service.feature index a8ca2429..88a0bd4b 100644 --- a/service/features/service.feature +++ b/service/features/service.feature @@ -140,7 +140,7 @@ Feature: VxFlex OS CSI interface And the Controller has no connection When I invalidate the Probe cache And I call Probe - Then the error contains "unable to login to VxFlexOS Gateway" + Then the error contains "unable to login to PowerFlex Gateway" Scenario Outline: Probe Call with various errors Given a VxFlexOS service @@ -151,11 +151,11 @@ Feature: VxFlex OS CSI interface Examples: | error | msg | - | "NoEndpointError" | "missing VxFlexOS Gateway endpoint" | - | "NoUserError" | "missing VxFlexOS MDM user" | - | "NoPasswordError" | "missing VxFlexOS MDM password" | - | "NoSysNameError" | "missing VxFlexOS system name" | - | "WrongSysNameError" | "unable to find matching VxFlexOS system name" | + | "NoEndpointError" | "missing PowerFlex Gateway endpoint" | + | "NoUserError" | "missing PowerFlex MDM user" | + | "NoPasswordError" | "missing PowerFlex MDM password" | + | "NoSysNameError" | "missing PowerFlex system name" | + | "WrongSysNameError" | "unable to find matching PowerFlex system name" | # This injected error fails on Windows with no SDC but passes on Linux with SDC @@ -490,6 +490,18 @@ Feature: VxFlex OS CSI interface Given a VxFlexOS service When I call Probe And I call GetCapacity with storage pool "" + + Scenario: Call GetCapacity for a system using Availability zones + Given a VxFlexOS service + And I use config + When I call Probe + And I call GetCapacity with Availability Zone + Then the error contains + + Examples: + | config | zone-key | zone-name | errorMsg | + | "multi_az" | "zone.csi-vxflexos.dellemc.com" | "zoneA" | "none" | + | "multi_az" | "zone.csi-vxflexos.dellemc.com" | "badZone" | "could not find an array assigned to zone 'badZone'" | Scenario: Call GetCapacity with valid Storage Pool Name Given a VxFlexOS service @@ -1145,7 +1157,7 @@ Feature: VxFlex OS CSI interface Scenario: Call getSystemName, should get error Unable to probe system with ID Given a VxFlexOS service When I call getSystemNameError - Then the error contains "missing VxFlexOS system name" + Then the error contains "missing PowerFlex system name" Scenario: Call getSystemName, should get Found system Name: mocksystem Given a VxFlexOS service @@ -1178,14 +1190,14 @@ Feature: VxFlex OS CSI interface And I do not have a gateway connection And I do not have a valid gateway endpoint When I Call nodeGetAllSystems - Then the error contains "missing VxFlexOS Gateway endpoint" + Then the error contains "missing PowerFlex Gateway endpoint" Scenario: Call Node getAllSystems Given a VxFlexOS service And I do not have a gateway connection And I do not have a valid gateway password When I Call nodeGetAllSystems - Then the error contains "missing VxFlexOS MDM password" + Then the error contains "missing PowerFlex MDM password" Scenario: Call evalsymlinks Given a VxFlexOS service @@ -1251,7 +1263,7 @@ Feature: VxFlex OS CSI interface And I invalidate the Probe cache When I call BeforeServe # Get different error message on Windows vs. Linux - Then the error contains "unable to login to VxFlexOS Gateway" + Then the error contains "unable to login to PowerFlex Gateway" Scenario: Test getArrayConfig with invalid config file Given an invalid config @@ -1606,3 +1618,13 @@ Feature: VxFlex OS CSI interface Examples: | name | config | errorMsg | | "volume1" | "multi_az" | "none" | + + Scenario: Probe all systems using availability zones + Given a VxFlexOS service + And I use config + When I call systemProbeAll in mode + Then the error contains + Examples: + | config | mode | errorMsg | + | "multi_az" | "node" | "none" | + | "multi_az" | "controller" | "none" | \ No newline at end of file diff --git a/service/identity.go b/service/identity.go index b7ec6a2f..052a92d8 100644 --- a/service/identity.go +++ b/service/identity.go @@ -1,4 +1,4 @@ -// Copyright © 2019-2022 Dell Inc. or its subsidiaries. All Rights Reserved. +// Copyright © 2019-2024 Dell Inc. or its subsidiaries. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -77,7 +77,6 @@ func (s *service) Probe( _ *csi.ProbeRequest) ( *csi.ProbeResponse, error, ) { - Log.Debug("Probe called") if !strings.EqualFold(s.mode, "node") { Log.Debug("systemProbe") if err := s.systemProbeAll(ctx); err != nil { @@ -92,10 +91,9 @@ func (s *service) Probe( return nil, err } } - ready := new(wrapperspb.BoolValue) - ready.Value = true - rep := new(csi.ProbeResponse) - rep.Ready = ready + rep := &csi.ProbeResponse{ + Ready: wrapperspb.Bool(true), + } Log.Debug(fmt.Sprintf("Probe returning: %v", rep.Ready.GetValue())) return rep, nil diff --git a/service/node.go b/service/node.go index 31162df0..5415be59 100644 --- a/service/node.go +++ b/service/node.go @@ -766,6 +766,11 @@ func (s *service) NodeGetInfo( if zone, ok := labels[s.opts.zoneLabelKey]; ok { topology[s.opts.zoneLabelKey] = zone + + err = s.SetPodZoneLabel(ctx, topology) + if err != nil { + Log.Warnf("Unable to set availability zone label '%s:%s' for this pod", topology[s.opts.zoneLabelKey], zone) + } } for _, array := range s.opts.arrays { diff --git a/service/service.go b/service/service.go index ddd79830..ca294761 100644 --- a/service/service.go +++ b/service/service.go @@ -715,15 +715,17 @@ func (s *service) doProbe(ctx context.Context) error { px.Lock() defer px.Unlock() - if !strings.EqualFold(s.mode, "node") { + if !s.isNodeMode() { + Log.Info("[doProbe] controllerProbe") if err := s.systemProbeAll(ctx); err != nil { return err } } // Do a node probe - if !strings.EqualFold(s.mode, "controller") { + if !s.isControllerMode() { // Probe all systems managed by driver + Log.Info("[doProbe] nodeProbe") if err := s.systemProbeAll(ctx); err != nil { return err } @@ -1853,6 +1855,49 @@ func (s *service) GetNodeLabels(_ context.Context) (map[string]string, error) { return node.Labels, nil } +func (s *service) SetPodZoneLabel(ctx context.Context, zoneLabel map[string]string) error { + if K8sClientset == nil { + err := k8sutils.CreateKubeClientSet() + if err != nil { + return status.Error(codes.Internal, GetMessage("init client failed with error: %v", err)) + } + K8sClientset = k8sutils.Clientset + } + + // access the API to fetch node object + pods, err := K8sClientset.CoreV1().Pods(DriverNamespace).List(ctx, v1.ListOptions{}) + if err != nil { + return status.Error(codes.Internal, GetMessage("Unable to fetch the node labels. Error: %v", err)) + } + + podName := "" + for _, pod := range pods.Items { + if pod.Spec.NodeName == s.opts.KubeNodeName && pod.Labels["app"] != "" { + // only add labels to node pods. Controller pod is not restricted to a zone + if strings.Contains(pod.Name, "node") { + podName = pod.Name + } + } + } + + pod, err := K8sClientset.CoreV1().Pods(DriverNamespace).Get(ctx, podName, v1.GetOptions{}) + if err != nil { + return status.Error(codes.Internal, GetMessage("Unable to fetch the node labels. Error: %v", err)) + } + + for key, value := range zoneLabel { + Log.Printf("Setting Label: Key: %s, Value: %s for pod: %s\n", key, value, podName) + pod.Labels[key] = value + } + + _, err = K8sClientset.CoreV1().Pods(DriverNamespace).Update(ctx, pod, v1.UpdateOptions{}) + if err != nil { + return status.Error(codes.Internal, GetMessage("Unable to update the node labels. Error: %v", err)) + } + + return nil +} + func (s *service) GetNodeUID(_ context.Context) (string, error) { if K8sClientset == nil { err := k8sutils.CreateKubeClientSet() @@ -1909,3 +1954,18 @@ func getZoneKeyLabelFromSecret(arrays map[string]*ArrayConnectionData) (string, return zoneKeyLabel, nil } + +// isControllerMode returns true if the mode property of service s is set to "node", false otherwise. +func (s *service) isNodeMode() bool { + return strings.EqualFold(s.mode, "node") +} + +// isControllerMode returns true if the mode property of service s is set to "controller", false otherwise. +func (s *service) isControllerMode() bool { + return strings.EqualFold(s.mode, "controller") +} + +// isInZone returns true if the array is configured for use in the provided zoneName, false otherwise. +func (array *ArrayConnectionData) isInZone(zoneName string) bool { + return array.AvailabilityZone != nil && array.AvailabilityZone.Name == ZoneName(zoneName) +} diff --git a/service/service_test.go b/service/service_test.go index ee3809cb..1430f86d 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -1,4 +1,4 @@ -// Copyright © 2019-2022 Dell Inc. or its subsidiaries. All Rights Reserved. +// Copyright © 2019-2024 Dell Inc. or its subsidiaries. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,13 +14,20 @@ package service import ( + "context" "fmt" "net/http" "os" + "sync" "testing" "time" "github.com/cucumber/godog" + sio "github.com/dell/goscaleio" + siotypes "github.com/dell/goscaleio/types/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" ) func TestMain(m *testing.M) { @@ -56,3 +63,216 @@ func TestMain(m *testing.M) { os.Exit(status) } + +func Test_service_SetPodZoneLabel(t *testing.T) { + type fields struct { + opts Opts + adminClients map[string]*sio.Client + systems map[string]*sio.System + mode string + volCache []*siotypes.Volume + volCacheSystemID string + snapCache []*siotypes.Volume + snapCacheSystemID string + privDir string + storagePoolIDToName map[string]string + statisticsCounter int + volumePrefixToSystems map[string][]string + connectedSystemNameToID map[string]string + } + + type args struct { + ctx context.Context + zoneLabel map[string]string + } + + const validZoneName = "zoneA" + const validZoneLabelKey = "topology.kubernetes.io/zone" + const validAppName = "test-node-pod" + const validAppLabelKey = "app" + const validNodeName = "kube-node-name" + validAppLabels := map[string]string{validAppLabelKey: validAppName} + + tests := map[string]struct { + fields fields + args args + initTest func(s *service) + wantErr bool + }{ + "successfully add zone labels to a pod": { + // happy path test + wantErr: false, + args: args{ + ctx: context.Background(), + zoneLabel: map[string]string{ + validZoneLabelKey: validZoneName, + }, + }, + fields: fields{ + opts: Opts{ + KubeNodeName: validNodeName, + }, + }, + initTest: func(s *service) { + // setup fake k8s client and create a pod to perform tests against + K8sClientset = fake.NewSimpleClientset() + podClient := K8sClientset.CoreV1().Pods(DriverNamespace) + + // create test pod + _, err := podClient.Create(context.Background(), &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: validAppName, + Labels: validAppLabels, + }, + Spec: v1.PodSpec{ + NodeName: s.opts.KubeNodeName, + }, + }, metav1.CreateOptions{}) + if err != nil { + t.Errorf("error creating test pod error = %v", err) + } + }, + }, + "when 'list pods' k8s client request fails": { + // Attempt to set pod labels when the k8s client cannot get pods + wantErr: true, + args: args{ + ctx: context.Background(), + zoneLabel: map[string]string{ + validZoneLabelKey: validZoneName, + }, + }, + fields: fields{ + opts: Opts{ + KubeNodeName: validNodeName, + }, + }, + initTest: func(_ *service) { + // create a client, but do not create any pods so the request + // to list pods fails + K8sClientset = fake.NewSimpleClientset() + }, + }, + "clientset is nil and fails to create one": { + wantErr: true, + args: args{ + ctx: context.Background(), + zoneLabel: map[string]string{ + validZoneLabelKey: validZoneName, + }, + }, + fields: fields{ + opts: Opts{ + KubeNodeName: validNodeName, + }, + }, + initTest: func(_ *service) { + // setup clientset to nil to force creation + // Creation should fail because tests are not run in a cluster + K8sClientset = nil + }, + }, + } + + for testName, tt := range tests { + t.Run(testName, func(t *testing.T) { + s := &service{ + opts: tt.fields.opts, + adminClients: tt.fields.adminClients, + systems: tt.fields.systems, + mode: tt.fields.mode, + volCache: tt.fields.volCache, + volCacheRWL: sync.RWMutex{}, + volCacheSystemID: tt.fields.volCacheSystemID, + snapCache: tt.fields.snapCache, + snapCacheRWL: sync.RWMutex{}, + snapCacheSystemID: tt.fields.snapCacheSystemID, + privDir: tt.fields.privDir, + storagePoolIDToName: tt.fields.storagePoolIDToName, + statisticsCounter: tt.fields.statisticsCounter, + volumePrefixToSystems: tt.fields.volumePrefixToSystems, + connectedSystemNameToID: tt.fields.connectedSystemNameToID, + } + + tt.initTest(s) + err := s.SetPodZoneLabel(tt.args.ctx, tt.args.zoneLabel) + if (err != nil) != tt.wantErr { + t.Errorf("service.SetPodZoneLabel() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestArrayConnectionData_isInZone(t *testing.T) { + type fields struct { + SystemID string + Username string + Password string + Endpoint string + SkipCertificateValidation bool + Insecure bool + IsDefault bool + AllSystemNames string + NasName string + AvailabilityZone *AvailabilityZone + } + type args struct { + zoneName string + } + tests := map[string]struct { + fields fields + args args + want bool + }{ + "success": { + want: true, + fields: fields{ + AvailabilityZone: &AvailabilityZone{ + LabelKey: "topology.kubernetes.io/zone", + Name: "zoneA", + }, + }, + args: args{ + zoneName: "zoneA", + }, + }, + "availability zone is not used": { + want: false, + fields: fields{}, + args: args{ + zoneName: "zoneA", + }, + }, + "zone names do not match": { + want: false, + fields: fields{ + AvailabilityZone: &AvailabilityZone{ + LabelKey: "topology.kubernetes.io/zone", + Name: "zoneA", + }, + }, + args: args{ + zoneName: "zoneB", + }, + }, + } + for testName, tt := range tests { + t.Run(testName, func(t *testing.T) { + array := &ArrayConnectionData{ + SystemID: tt.fields.SystemID, + Username: tt.fields.Username, + Password: tt.fields.Password, + Endpoint: tt.fields.Endpoint, + SkipCertificateValidation: tt.fields.SkipCertificateValidation, + Insecure: tt.fields.Insecure, + IsDefault: tt.fields.IsDefault, + AllSystemNames: tt.fields.AllSystemNames, + NasName: tt.fields.NasName, + AvailabilityZone: tt.fields.AvailabilityZone, + } + if got := array.isInZone(tt.args.zoneName); got != tt.want { + t.Errorf("ArrayConnectionData.isInZone() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/service/step_defs_test.go b/service/step_defs_test.go index 9cdefbbd..3a7f138d 100644 --- a/service/step_defs_test.go +++ b/service/step_defs_test.go @@ -1,4 +1,4 @@ -// Copyright © 2019-2023 Dell Inc. or its subsidiaries. All Rights Reserved. +// Copyright © 2019-2024 Dell Inc. or its subsidiaries. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -326,9 +326,9 @@ func (f *feature) getService() *service { return f.service } var opts Opts - ctx := new(context.Context) + ctx := context.Background() var err error - opts.arrays, err = getArrayConfig(*ctx) + opts.arrays, err = getArrayConfig(ctx) if err != nil { log.Printf("Read arrays from config file failed: %s\n", err) } @@ -462,9 +462,9 @@ func (f *feature) aValidDynamicLogChange(file, expectedLevel string) error { // GetPluginInfo func (f *feature) iCallGetPluginInfo() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.GetPluginInfoRequest) - f.getPluginInfoResponse, f.err = f.service.GetPluginInfo(*ctx, req) + f.getPluginInfoResponse, f.err = f.service.GetPluginInfo(ctx, req) if f.err != nil { return f.err } @@ -513,9 +513,9 @@ func (f *feature) aValidGetPlugInfoResponseIsReturned() error { } func (f *feature) iCallGetPluginCapabilities() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.GetPluginCapabilitiesRequest) - f.getPluginCapabilitiesResponse, f.err = f.service.GetPluginCapabilities(*ctx, req) + f.getPluginCapabilitiesResponse, f.err = f.service.GetPluginCapabilities(ctx, req) if f.err != nil { return f.err } @@ -538,12 +538,12 @@ func (f *feature) aValidGetPluginCapabilitiesResponseIsReturned() error { } func (f *feature) iCallProbe() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.ProbeRequest) f.checkGoRoutines("before probe") f.service.opts.AutoProbe = true f.service.mode = "controller" - f.probeResponse, f.err = f.service.Probe(*ctx, req) + f.probeResponse, f.err = f.service.Probe(ctx, req) f.checkGoRoutines("after probe") return nil } @@ -700,7 +700,7 @@ func (f *feature) iSpecifyCreateVolumeMountRequest(fstype string) error { } func (f *feature) iCallCreateVolume(name string) error { - ctx := new(context.Context) + ctx := context.Background() if f.createVolumeRequest == nil { req := getTypicalCreateVolumeRequest() f.createVolumeRequest = req @@ -715,7 +715,7 @@ func (f *feature) iCallCreateVolume(name string) error { fmt.Println("I am in iCallCreateVolume fn.....") - f.createVolumeResponse, f.err = f.service.CreateVolume(*ctx, req) + f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { log.Printf("CreateVolume called failed: %s\n", f.err.Error()) } @@ -727,7 +727,7 @@ func (f *feature) iCallCreateVolume(name string) error { } func (f *feature) iCallValidateVolumeHostConnectivity() error { - ctx := new(context.Context) + ctx := context.Background() sdcID := f.service.opts.SdcGUID sdcGUID := strings.ToUpper(sdcID) @@ -764,7 +764,7 @@ func (f *feature) iCallValidateVolumeHostConnectivity() error { VolumeIds: volIDs, } - connect, err := f.service.ValidateVolumeHostConnectivity(*ctx, req) + connect, err := f.service.ValidateVolumeHostConnectivity(ctx, req) if err != nil { f.err = errors.New(err.Error()) return nil @@ -1026,7 +1026,7 @@ func (f *feature) iSpecifyNoStoragePool() error { } func (f *feature) iCallCreateVolumeSize(name string, size int64) error { - ctx := new(context.Context) + ctx := context.Background() var req *csi.CreateVolumeRequest if f.createVolumeRequest == nil { req = getTypicalCreateVolumeRequest() @@ -1040,7 +1040,7 @@ func (f *feature) iCallCreateVolumeSize(name string, size int64) error { req.Name = name f.createVolumeRequest = req - f.createVolumeResponse, f.err = f.service.CreateVolume(*ctx, req) + f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { log.Printf("CreateVolumeSize called failed: %s\n", f.err.Error()) } @@ -1052,7 +1052,7 @@ func (f *feature) iCallCreateVolumeSize(name string, size int64) error { } func (f *feature) iCallCreateVolumeSizeNFS(name string, size int64) error { - ctx := new(context.Context) + ctx := context.Background() var req *csi.CreateVolumeRequest if f.createVolumeRequest == nil { req = getTypicalNFSCreateVolumeRequest() @@ -1066,7 +1066,7 @@ func (f *feature) iCallCreateVolumeSizeNFS(name string, size int64) error { req.Name = name f.createVolumeRequest = req - f.createVolumeResponse, f.err = f.service.CreateVolume(*ctx, req) + f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { log.Printf("CreateVolumeSize called failed: %s\n", f.err.Error()) } @@ -1610,7 +1610,7 @@ func (f *feature) getControllerDeleteVolumeRequestNFS(accessType string) *csi.De } func (f *feature) iCallPublishVolumeWith(arg1 string) error { - ctx := new(context.Context) + ctx := context.Background() req := f.publishVolumeRequest if f.publishVolumeRequest == nil { req = f.getControllerPublishVolumeRequest(arg1) @@ -1618,7 +1618,7 @@ func (f *feature) iCallPublishVolumeWith(arg1 string) error { } log.Printf("Calling controllerPublishVolume") - f.publishVolumeResponse, f.err = f.service.ControllerPublishVolume(*ctx, req) + f.publishVolumeResponse, f.err = f.service.ControllerPublishVolume(ctx, req) if f.err != nil { log.Printf("PublishVolume call failed: %s\n", f.err.Error()) } @@ -1626,7 +1626,7 @@ func (f *feature) iCallPublishVolumeWith(arg1 string) error { } func (f *feature) iCallPublishVolumeWithNFS(arg1 string) error { - ctx := new(context.Context) + ctx := context.Background() req := f.publishVolumeRequest if f.publishVolumeRequest == nil { req = f.getControllerPublishVolumeRequestNFS(arg1) @@ -1683,7 +1683,7 @@ func (f *feature) iCallPublishVolumeWithNFS(arg1 string) error { } log.Printf("Calling controllerPublishVolume") - f.publishVolumeResponse, f.err = f.service.ControllerPublishVolume(*ctx, req) + f.publishVolumeResponse, f.err = f.service.ControllerPublishVolume(ctx, req) if f.err != nil { log.Printf("PublishVolume call failed: %s\n", f.err.Error()) } @@ -1806,14 +1806,14 @@ func (f *feature) getControllerUnpublishVolumeRequestNFS() *csi.ControllerUnpubl } func (f *feature) iCallUnpublishVolume() error { - ctx := new(context.Context) + ctx := context.Background() req := f.unpublishVolumeRequest if f.unpublishVolumeRequest == nil { req = f.getControllerUnpublishVolumeRequest() f.unpublishVolumeRequest = req } log.Printf("Calling controllerUnpublishVolume: %s", req.VolumeId) - f.unpublishVolumeResponse, f.err = f.service.ControllerUnpublishVolume(*ctx, req) + f.unpublishVolumeResponse, f.err = f.service.ControllerUnpublishVolume(ctx, req) if f.err != nil { log.Printf("UnpublishVolume call failed: %s\n", f.err.Error()) } @@ -1821,7 +1821,7 @@ func (f *feature) iCallUnpublishVolume() error { } func (f *feature) iCallUnpublishVolumeNFS() error { - ctx := new(context.Context) + ctx := context.Background() req := f.unpublishVolumeRequest if f.unpublishVolumeRequest == nil { req = f.getControllerUnpublishVolumeRequestNFS() @@ -1855,7 +1855,7 @@ func (f *feature) iCallUnpublishVolumeNFS() error { } log.Printf("Calling controllerUnpublishVolume: %s", req.VolumeId) - f.unpublishVolumeResponse, f.err = f.service.ControllerUnpublishVolume(*ctx, req) + f.unpublishVolumeResponse, f.err = f.service.ControllerUnpublishVolume(ctx, req) if f.err != nil { log.Printf("UnpublishVolume call failed: %s\n", f.err.Error()) } @@ -1877,50 +1877,50 @@ func (f *feature) theNumberOfSDCMappingsIs(arg1 int) error { } func (f *feature) iCallNodeGetInfo() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.NodeGetInfoRequest) f.service.opts.SdcGUID = "9E56672F-2F4B-4A42-BFF4-88B6846FBFDA" GetNodeLabels = mockGetNodeLabels GetNodeUID = mockGetNodeUID - f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(*ctx, req) + f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(ctx, req) return nil } func (f *feature) iCallNodeGetInfoWithValidVolumeLimitNodeLabels() error { f.setFakeNode() - ctx := new(context.Context) + ctx := context.Background() req := new(csi.NodeGetInfoRequest) f.service.opts.SdcGUID = "9E56672F-2F4B-4A42-BFF4-88B6846FBFDA" GetNodeLabels = mockGetNodeLabelsWithVolumeLimits - f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(*ctx, req) + f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(ctx, req) fmt.Printf("MaxVolumesPerNode: %v", f.nodeGetInfoResponse.MaxVolumesPerNode) return nil } func (f *feature) iCallNodeGetInfoWithInvalidVolumeLimitNodeLabels() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.NodeGetInfoRequest) f.service.opts.SdcGUID = "9E56672F-2F4B-4A42-BFF4-88B6846FBFDA" GetNodeLabels = mockGetNodeLabelsWithInvalidVolumeLimits - f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(*ctx, req) + f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(ctx, req) return nil } func (f *feature) iCallNodeGetInfoWithValidNodeUID() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.NodeGetInfoRequest) GetNodeUID = mockGetNodeUID f.service.opts.SdcGUID = "" - f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(*ctx, req) + f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(ctx, req) fmt.Printf("NodeGetInfoResponse: %v", f.nodeGetInfoResponse) return nil } func (f *feature) iCallGetNodeUID() error { f.setFakeNode() - ctx := new(context.Context) + ctx := context.Background() nodeUID := "" - nodeUID, err := f.service.GetNodeUID(*ctx) + nodeUID, err := f.service.GetNodeUID(ctx) fmt.Printf("Node UID: %v", nodeUID) if err != nil { @@ -2012,8 +2012,8 @@ func (f *feature) iCallGetNodeLabelsWithInvalidNode() error { func (f *feature) iCallGetNodeLabelsWithUnsetKubernetesClient() error { K8sClientset = nil - ctx := new(context.Context) - f.nodeLabels, f.err = f.service.GetNodeLabels(*ctx) + ctx := context.Background() + f.nodeLabels, f.err = f.service.GetNodeLabels(ctx) return nil } @@ -2025,17 +2025,17 @@ func (f *feature) iCallGetNodeUIDWithInvalidNode() error { func (f *feature) iCallGetNodeUIDWithUnsetKubernetesClient() error { K8sClientset = nil - ctx := new(context.Context) - f.nodeUID, f.err = f.service.GetNodeUID(*ctx) + ctx := context.Background() + f.nodeUID, f.err = f.service.GetNodeUID(ctx) return nil } func (f *feature) iCallNodeProbe() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.ProbeRequest) f.checkGoRoutines("before probe") f.service.mode = "node" - f.probeResponse, f.err = f.service.Probe(*ctx, req) + f.probeResponse, f.err = f.service.Probe(ctx, req) f.checkGoRoutines("after probe") return nil } @@ -2082,14 +2082,14 @@ func (f *feature) theVolumeLimitIsSet() error { } func (f *feature) iCallDeleteVolumeWith(arg1 string) error { - ctx := new(context.Context) + ctx := context.Background() req := f.deleteVolumeRequest if f.deleteVolumeRequest == nil { req = f.getControllerDeleteVolumeRequest(arg1) f.deleteVolumeRequest = req } log.Printf("Calling DeleteVolume") - f.deleteVolumeResponse, f.err = f.service.DeleteVolume(*ctx, req) + f.deleteVolumeResponse, f.err = f.service.DeleteVolume(ctx, req) if f.err != nil { log.Printf("DeleteVolume called failed: %s\n", f.err.Error()) } @@ -2097,14 +2097,14 @@ func (f *feature) iCallDeleteVolumeWith(arg1 string) error { } func (f *feature) iCallDeleteVolumeWithBad(arg1 string) error { - ctx := new(context.Context) + ctx := context.Background() req := f.deleteVolumeRequest if f.deleteVolumeRequest == nil { req = f.getControllerDeleteVolumeRequestBad(arg1) f.deleteVolumeRequest = req } log.Printf("Calling DeleteVolume") - f.deleteVolumeResponse, f.err = f.service.DeleteVolume(*ctx, req) + f.deleteVolumeResponse, f.err = f.service.DeleteVolume(ctx, req) if f.err != nil { log.Printf("DeleteVolume called failed: %s\n", f.err.Error()) } @@ -2112,14 +2112,14 @@ func (f *feature) iCallDeleteVolumeWithBad(arg1 string) error { } func (f *feature) iCallDeleteVolumeNFSWith(arg1 string) error { - ctx := new(context.Context) + ctx := context.Background() req := f.deleteVolumeRequest if f.deleteVolumeRequest == nil { req = f.getControllerDeleteVolumeRequestNFS(arg1) f.deleteVolumeRequest = req } log.Printf("Calling DeleteVolume") - f.deleteVolumeResponse, f.err = f.service.DeleteVolume(*ctx, req) + f.deleteVolumeResponse, f.err = f.service.DeleteVolume(ctx, req) if f.err != nil { log.Printf("DeleteVolume called failed: %s\n", f.err.Error()) } @@ -2147,7 +2147,7 @@ func (f *feature) theVolumeIsAlreadyMappedToAnSDC() error { } func (f *feature) iCallGetCapacityWithStoragePool(arg1 string) error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.GetCapacityRequest) if arg1 != "" { parameters := make(map[string]string) @@ -2155,7 +2155,31 @@ func (f *feature) iCallGetCapacityWithStoragePool(arg1 string) error { req.Parameters = parameters } log.Printf("Calling GetCapacity") - f.getCapacityResponse, f.err = f.service.GetCapacity(*ctx, req) + f.getCapacityResponse, f.err = f.service.GetCapacity(ctx, req) + if f.err != nil { + log.Printf("GetCapacity call failed: %s\n", f.err.Error()) + return nil + } + return nil +} + +func (f *feature) iCallGetCapacityWithAvailabilityZone(zoneLabelKey, zoneName string) error { + ctx := context.Background() + req := new(csi.GetCapacityRequest) + + // need to make sure parameters aren't empty. + // This is a parameter taken from a running driver. + parameters := make(map[string]string) + parameters["csi.storage.k8s.io/fstype"] = "xfs" + req.Parameters = parameters + req.AccessibleTopology = &csi.Topology{ + Segments: map[string]string{ + zoneLabelKey: zoneName, + }, + } + + log.Printf("Calling GetCapacity") + f.getCapacityResponse, f.err = f.service.GetCapacity(ctx, req) if f.err != nil { log.Printf("GetCapacity call failed: %s\n", f.err.Error()) return nil @@ -2205,10 +2229,10 @@ func (f *feature) iCallControllerGetCapabilities(isHealthMonitorEnabled string) if isHealthMonitorEnabled == "true" { f.service.opts.IsHealthMonitorEnabled = true } - ctx := new(context.Context) + ctx := context.Background() req := new(csi.ControllerGetCapabilitiesRequest) log.Printf("Calling ControllerGetCapabilities") - f.controllerGetCapabilitiesResponse, f.err = f.service.ControllerGetCapabilities(*ctx, req) + f.controllerGetCapabilitiesResponse, f.err = f.service.ControllerGetCapabilities(ctx, req) if f.err != nil { log.Printf("ControllerGetCapabilities call failed: %s\n", f.err.Error()) return f.err @@ -2263,7 +2287,7 @@ func (f *feature) iCallListVolumesWith(maxEntriesString, startingToken string) e return err } - ctx := new(context.Context) + ctx := context.Background() req := f.listVolumesRequest if f.listVolumesRequest == nil { switch st := startingToken; st { @@ -2286,7 +2310,7 @@ func (f *feature) iCallListVolumesWith(maxEntriesString, startingToken string) e f.listVolumesRequest = req } log.Printf("Calling ListVolumes with req=%+v", f.listVolumesRequest) - f.listVolumesResponse, f.err = f.service.ListVolumes(*ctx, req) + f.listVolumesResponse, f.err = f.service.ListVolumes(ctx, req) if f.err != nil { log.Printf("ListVolume called failed: %s\n", f.err.Error()) } else { @@ -2349,7 +2373,7 @@ func (f *feature) aValidControllerGetCapabilitiesResponseIsReturned() error { } func (f *feature) iCallCloneVolume() error { - ctx := new(context.Context) + ctx := context.Background() req := getTypicalCreateVolumeRequest() req.Name = "clone" if f.invalidVolumeID { @@ -2366,7 +2390,7 @@ func (f *feature) iCallCloneVolume() error { req.VolumeContentSource = new(csi.VolumeContentSource) req.VolumeContentSource.Type = &csi.VolumeContentSource_Volume{Volume: source} req.AccessibilityRequirements = new(csi.TopologyRequirement) - f.createVolumeResponse, f.err = f.service.CreateVolume(*ctx, req) + f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { fmt.Printf("Error on CreateVolume from volume: %s\n", f.err.Error()) } @@ -2376,7 +2400,7 @@ func (f *feature) iCallCloneVolume() error { //nolint:revive func (f *feature) iCallValidateVolumeCapabilitiesWithVoltypeAccessFstype(voltype, access, fstype string) error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.ValidateVolumeCapabilitiesRequest) if f.invalidVolumeID || f.createVolumeResponse == nil { req.VolumeId = badVolumeID2 @@ -2417,7 +2441,7 @@ func (f *feature) iCallValidateVolumeCapabilitiesWithVoltypeAccessFstype(voltype capabilities = append(capabilities, capability) req.VolumeCapabilities = capabilities log.Printf("Calling ValidateVolumeCapabilities %#v", accessMode) - f.validateVolumeCapabilitiesResponse, f.err = f.service.ValidateVolumeCapabilities(*ctx, req) + f.validateVolumeCapabilitiesResponse, f.err = f.service.ValidateVolumeCapabilities(ctx, req) if f.err != nil { return nil } @@ -2896,8 +2920,8 @@ func (f *feature) iCallNodePublishVolumeNFS(arg1 string) error { func (f *feature) iCallUnmountPrivMount() error { gofsutil.GOFSMock.InduceGetMountsError = true - ctx := new(context.Context) - err := unmountPrivMount(*ctx, nil, "/foo/bar") + ctx := context.Background() + err := unmountPrivMount(ctx, nil, "/foo/bar") fmt.Printf("unmountPrivMount getMounts error: %s\n", err.Error()) // getMounts induced error if err != nil { @@ -2917,7 +2941,7 @@ func (f *feature) iCallUnmountPrivMount() error { gofsutil.GOFSMock.InduceGetMountsError = false gofsutil.GOFSMock.InduceUnmountError = true - err = unmountPrivMount(*ctx, nil, target) + err = unmountPrivMount(ctx, nil, target) fmt.Printf("unmountPrivMount unmount error: %s\n", err) if err != nil { f.err = errors.New("error in unmountPrivMount") @@ -3271,9 +3295,9 @@ func (f *feature) iCallBeforeServe() error { } func (f *feature) iCallNodeStageVolume() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.NodeStageVolumeRequest) - _, f.err = f.service.NodeStageVolume(*ctx, req) + _, f.err = f.service.NodeStageVolume(ctx, req) return nil } @@ -3322,7 +3346,7 @@ func (f *feature) iCallNodeExpandVolume(volPath string) error { } func (f *feature) iCallNodeGetVolumeStats() error { - ctx := new(context.Context) + ctx := context.Background() VolumeID := sdcVolume1 VolumePath := datadir @@ -3355,7 +3379,7 @@ func (f *feature) iCallNodeGetVolumeStats() error { req := &csi.NodeGetVolumeStatsRequest{VolumeId: VolumeID, VolumePath: VolumePath} - f.nodeGetVolumeStatsResponse, f.err = f.service.NodeGetVolumeStats(*ctx, req) + f.nodeGetVolumeStatsResponse, f.err = f.service.NodeGetVolumeStats(ctx, req) return nil } @@ -3461,12 +3485,12 @@ func (f *feature) iCallNodeUnstageVolumeWith(errStr string) error { } func (f *feature) iCallNodeGetCapabilities(isHealthMonitorEnabled string) error { - ctx := new(context.Context) + ctx := context.Background() if isHealthMonitorEnabled == "true" { f.service.opts.IsHealthMonitorEnabled = true } req := new(csi.NodeGetCapabilitiesRequest) - f.nodeGetCapabilitiesResponse, f.err = f.service.NodeGetCapabilities(*ctx, req) + f.nodeGetCapabilitiesResponse, f.err = f.service.NodeGetCapabilities(ctx, req) return nil } @@ -3657,7 +3681,7 @@ func (f *feature) aValidCreateVolumeSnapshotGroupResponse() error { } func (f *feature) iCallCreateSnapshot(snapName string) error { - ctx := new(context.Context) + ctx := context.Background() if len(f.volumeIDList) == 0 { f.volumeIDList = append(f.volumeIDList, "00000000") @@ -3692,15 +3716,15 @@ func (f *feature) iCallCreateSnapshot(snapName string) error { } fmt.Println("snapName is: ", snapName) - fmt.Println("ctx: ", *ctx) + fmt.Println("ctx: ", ctx) fmt.Println("req: ", req) - f.createSnapshotResponse, f.err = f.service.CreateSnapshot(*ctx, req) + f.createSnapshotResponse, f.err = f.service.CreateSnapshot(ctx, req) return nil } func (f *feature) iCallCreateSnapshotNFS(snapName string) error { - ctx := new(context.Context) + ctx := context.Background() req := &csi.CreateSnapshotRequest{ SourceVolumeId: "14dbbf5617523654" + "/" + fileSystemNameToID["volume1"], @@ -3716,10 +3740,10 @@ func (f *feature) iCallCreateSnapshotNFS(snapName string) error { } fmt.Println("snapName is: ", snapName) - fmt.Println("ctx: ", *ctx) + fmt.Println("ctx: ", ctx) fmt.Println("req: ", req) - f.createSnapshotResponse, f.err = f.service.CreateSnapshot(*ctx, req) + f.createSnapshotResponse, f.err = f.service.CreateSnapshot(ctx, req) return nil } @@ -3743,7 +3767,7 @@ func (f *feature) aValidSnapshot() error { } func (f *feature) iCallDeleteSnapshot() error { - ctx := new(context.Context) + ctx := context.Background() req := &csi.DeleteSnapshotRequest{SnapshotId: goodSnapID, Secrets: make(map[string]string)} req.Secrets["x"] = "y" if f.invalidVolumeID { @@ -3751,12 +3775,12 @@ func (f *feature) iCallDeleteSnapshot() error { } else if f.noVolumeID { req.SnapshotId = "" } - _, f.err = f.service.DeleteSnapshot(*ctx, req) + _, f.err = f.service.DeleteSnapshot(ctx, req) return nil } func (f *feature) iCallDeleteSnapshotNFS() error { - ctx := new(context.Context) + ctx := context.Background() var req *csi.DeleteSnapshotRequest = new(csi.DeleteSnapshotRequest) if fileSystemNameToID["snap1"] == "" { req = &csi.DeleteSnapshotRequest{SnapshotId: "14dbbf5617523654" + "/" + "1111111", Secrets: make(map[string]string)} @@ -3765,7 +3789,7 @@ func (f *feature) iCallDeleteSnapshotNFS() error { } req.Secrets["x"] = "y" - _, f.err = f.service.DeleteSnapshot(*ctx, req) + _, f.err = f.service.DeleteSnapshot(ctx, req) return nil } @@ -3796,7 +3820,7 @@ func (f *feature) aValidSnapshotConsistencyGroup() error { } func (f *feature) iCallCreateVolumeFromSnapshot() error { - ctx := new(context.Context) + ctx := context.Background() req := getTypicalCreateVolumeRequest() req.Name = "volumeFromSnap" if f.wrongCapacity { @@ -3808,7 +3832,7 @@ func (f *feature) iCallCreateVolumeFromSnapshot() error { source := &csi.VolumeContentSource_SnapshotSource{SnapshotId: goodSnapID} req.VolumeContentSource = new(csi.VolumeContentSource) req.VolumeContentSource.Type = &csi.VolumeContentSource_Snapshot{Snapshot: source} - f.createVolumeResponse, f.err = f.service.CreateVolume(*ctx, req) + f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { fmt.Printf("Error on CreateVolume from snap: %s\n", f.err.Error()) } @@ -3849,7 +3873,7 @@ func (f *feature) iCallCloneVolumeForZones(volumeID string) error { } func (f *feature) iCallCreateVolumeFromSnapshotNFS() error { - ctx := new(context.Context) + ctx := context.Background() req := getTypicalNFSCreateVolumeRequest() req.Name = "volumeFromSnap" if f.wrongCapacity { @@ -3861,7 +3885,7 @@ func (f *feature) iCallCreateVolumeFromSnapshotNFS() error { source := &csi.VolumeContentSource_SnapshotSource{SnapshotId: "14dbbf5617523654" + "/" + fileSystemNameToID["snap1"]} req.VolumeContentSource = new(csi.VolumeContentSource) req.VolumeContentSource.Type = &csi.VolumeContentSource_Snapshot{Snapshot: source} - f.createVolumeResponse, f.err = f.service.CreateVolume(*ctx, req) + f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { fmt.Printf("Error on CreateVolume from snap: %s\n", f.err.Error()) } @@ -3903,7 +3927,7 @@ func (f *feature) iCallListSnapshotsWithMaxentriesAndStartingtoken(maxEntriesStr if err != nil { return nil } - ctx := new(context.Context) + ctx := context.Background() // ignoring integer overflow issue, will not be an issue if maxEntries is less than 2147483647 // #nosec G115 @@ -3911,7 +3935,7 @@ func (f *feature) iCallListSnapshotsWithMaxentriesAndStartingtoken(maxEntriesStr f.listSnapshotsRequest = req log.Printf("Calling ListSnapshots with req=%+v", f.listVolumesRequest) - f.listSnapshotsResponse, f.err = f.service.ListSnapshots(*ctx, req) + f.listSnapshotsResponse, f.err = f.service.ListSnapshots(ctx, req) if f.err != nil { log.Printf("ListSnapshots called failed: %s\n", f.err.Error()) } @@ -3924,7 +3948,7 @@ func (f *feature) iCallListSnapshotsForVolume(arg1 string) error { sourceVolumeID = altVolumeID } - ctx := new(context.Context) + ctx := context.Background() req := &csi.ListSnapshotsRequest{SourceVolumeId: sourceVolumeID} req.StartingToken = "0" req.MaxEntries = 100 @@ -3936,7 +3960,7 @@ func (f *feature) iCallListSnapshotsForVolume(arg1 string) error { f.listSnapshotsRequest = req log.Printf("Calling ListSnapshots with req=%+v", f.listSnapshotsRequest) - f.listSnapshotsResponse, f.err = f.service.ListSnapshots(*ctx, req) + f.listSnapshotsResponse, f.err = f.service.ListSnapshots(ctx, req) if f.err != nil { log.Printf("ListSnapshots called failed: %s\n", f.err.Error()) } @@ -3944,11 +3968,11 @@ func (f *feature) iCallListSnapshotsForVolume(arg1 string) error { } func (f *feature) iCallListSnapshotsForSnapshot(arg1 string) error { - ctx := new(context.Context) + ctx := context.Background() req := &csi.ListSnapshotsRequest{SnapshotId: arg1} f.listSnapshotsRequest = req log.Printf("Calling ListSnapshots with req=%+v", f.listVolumesRequest) - f.listSnapshotsResponse, f.err = f.service.ListSnapshots(*ctx, req) + f.listSnapshotsResponse, f.err = f.service.ListSnapshots(ctx, req) if f.err != nil { log.Printf("ListSnapshots called failed: %s\n", f.err.Error()) } @@ -4189,8 +4213,8 @@ func (f *feature) iCallGetSystemNameError() error { stepHandlersErrors.PodmonNodeProbeError = true // Unable to probe system with ID: - ctx := new(context.Context) - f.err = f.service.systemProbe(*ctx, badarray) + ctx := context.Background() + f.err = f.service.systemProbe(ctx, badarray) return nil } @@ -4204,9 +4228,9 @@ func (f *feature) iCallGetSystemName() error { func (f *feature) iCallNodeGetAllSystems() error { // lookup the system names for a couple of systems // This should not generate an error as systems without names are supported - ctx := new(context.Context) + ctx := context.Background() badarray := f.service.opts.arrays[arrayID] - f.err = f.service.systemProbe(*ctx, badarray) + f.err = f.service.systemProbe(ctx, badarray) return nil } @@ -4249,8 +4273,8 @@ func (f *feature) anInvalidMaxVolumesPerNode() error { } func (f *feature) iCallGetArrayConfig() error { - ctx := new(context.Context) - _, err := getArrayConfig(*ctx) + ctx := context.Background() + _, err := getArrayConfig(ctx) if err != nil { f.err = err } @@ -4265,8 +4289,8 @@ func (f *feature) iCallgetArrayInstallationID(systemID string) error { } func (f *feature) iCallSetQoSParameters(systemID string, sdcID string, bandwidthLimit string, iopsLimit string, volumeName string, csiVolID string, nodeID string) error { - ctx := new(context.Context) - f.err = f.service.setQoSParameters(*ctx, systemID, sdcID, bandwidthLimit, iopsLimit, volumeName, csiVolID, nodeID) + ctx := context.Background() + f.err = f.service.setQoSParameters(ctx, systemID, sdcID, bandwidthLimit, iopsLimit, volumeName, csiVolID, nodeID) if f.err != nil { fmt.Printf("error in setting QoS parameters for volume %s : %s\n", volumeName, f.err.Error()) } @@ -4307,10 +4331,40 @@ func (f *feature) iUseConfig(filename string) error { return nil } +func (f *feature) iCallSystemProbeAll(mode string) error { + // set the mode of the service + if mode == "controller" || mode == "node" { + f.service.mode = mode + } else { + return fmt.Errorf("mode '%s' is not a valid service mode. must be 'controller' or 'node'", mode) + } + + // Create a fake node with necessary availability zone labels + f.service.opts.KubeNodeName = "node1" + fakeNode := &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: f.service.opts.KubeNodeName, + UID: "1aa4c285-d41b-4911-bf3e-621253bfbade", + Labels: map[string]string{ + f.service.opts.zoneLabelKey: string(f.service.opts.arrays[arrayID].AvailabilityZone.Name), + }, + }, + } + thisNode, err := K8sClientset.CoreV1().Nodes().Create(context.Background(), fakeNode, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("could not create k8s node for test: err: %s", err.Error()) + } + // delete it when finished, otherwise it will fail to create nodes for subsequent tests + defer K8sClientset.CoreV1().Nodes().Delete(context.Background(), thisNode.Name, *&metav1.DeleteOptions{}) + + f.err = f.service.systemProbeAll(context.Background()) + return nil +} + func (f *feature) iCallGetReplicationCapabilities() error { req := &replication.GetReplicationCapabilityRequest{} - ctx := new(context.Context) - f.replicationCapabilitiesResponse, f.err = f.service.GetReplicationCapabilities(*ctx, req) + ctx := context.Background() + f.replicationCapabilitiesResponse, f.err = f.service.GetReplicationCapabilities(ctx, req) log.Printf("GetReplicationCapabilities returned %+v", f.replicationCapabilitiesResponse) return nil } @@ -4389,7 +4443,7 @@ func (f *feature) iSetApproveSdcEnabled(approveSDCEnabled string) error { } func (f *feature) iCallCreateRemoteVolume() error { - ctx := new(context.Context) + ctx := context.Background() req := &replication.CreateRemoteVolumeRequest{} if f.createVolumeResponse == nil { return errors.New("iCallCreateRemoteVolume: f.createVolumeResponse is nil") @@ -4405,7 +4459,7 @@ func (f *feature) iCallCreateRemoteVolume() error { f.service.WithRP(KeyReplicationRemoteStoragePool): "viki_pool_HDD_20181031", f.service.WithRP(KeyReplicationRemoteSystem): "15dbbf5617523655", } - _, f.err = f.service.CreateRemoteVolume(*ctx, req) + _, f.err = f.service.CreateRemoteVolume(ctx, req) if f.err != nil { fmt.Printf("CreateRemoteVolumeRequest returned error: %s", f.err) } @@ -4413,7 +4467,7 @@ func (f *feature) iCallCreateRemoteVolume() error { } func (f *feature) iCallDeleteLocalVolume(name string) error { - ctx := new(context.Context) + ctx := context.Background() replicatedVolName := "replicated-" + name volumeHandle := arrayID2 + "-" + volumeNameToID[replicatedVolName] @@ -4431,7 +4485,7 @@ func (f *feature) iCallDeleteLocalVolume(name string) error { VolumeHandle: volumeHandle, } - _, f.err = f.service.DeleteLocalVolume(*ctx, req) + _, f.err = f.service.DeleteLocalVolume(ctx, req) if f.err != nil { fmt.Printf("DeleteLocalVolume returned error: %s", f.err) } @@ -4440,7 +4494,7 @@ func (f *feature) iCallDeleteLocalVolume(name string) error { } func (f *feature) iCallCreateStorageProtectionGroup() error { - ctx := new(context.Context) + ctx := context.Background() parameters := make(map[string]string) // Must be repeatable. @@ -4481,12 +4535,12 @@ func (f *feature) iCallCreateStorageProtectionGroup() error { if stepHandlersErrors.BadVolIDError { req.VolumeHandle = "0%0" } - f.createStorageProtectionGroupResponse, f.err = f.service.CreateStorageProtectionGroup(*ctx, req) + f.createStorageProtectionGroupResponse, f.err = f.service.CreateStorageProtectionGroup(ctx, req) return nil } func (f *feature) iCallCreateStorageProtectionGroupWith(arg1, arg2, arg3 string) error { - ctx := new(context.Context) + ctx := context.Background() parameters := make(map[string]string) // Must be repeatable. @@ -4503,12 +4557,12 @@ func (f *feature) iCallCreateStorageProtectionGroupWith(arg1, arg2, arg3 string) Parameters: parameters, } - f.createStorageProtectionGroupResponse, f.err = f.service.CreateStorageProtectionGroup(*ctx, req) + f.createStorageProtectionGroupResponse, f.err = f.service.CreateStorageProtectionGroup(ctx, req) return nil } func (f *feature) iCallGetStorageProtectionGroupStatus() error { - ctx := new(context.Context) + ctx := context.Background() attributes := make(map[string]string) replicationGroupConsistMode = defaultConsistencyMode @@ -4518,13 +4572,13 @@ func (f *feature) iCallGetStorageProtectionGroupStatus() error { ProtectionGroupId: f.createStorageProtectionGroupResponse.LocalProtectionGroupId, ProtectionGroupAttributes: attributes, } - _, f.err = f.service.GetStorageProtectionGroupStatus(*ctx, req) + _, f.err = f.service.GetStorageProtectionGroupStatus(ctx, req) return nil } func (f *feature) iCallGetStorageProtectionGroupStatusWithStateAndMode(arg1, arg2 string) error { - ctx := new(context.Context) + ctx := context.Background() attributes := make(map[string]string) replicationGroupState = arg1 @@ -4535,7 +4589,7 @@ func (f *feature) iCallGetStorageProtectionGroupStatusWithStateAndMode(arg1, arg ProtectionGroupId: f.createStorageProtectionGroupResponse.LocalProtectionGroupId, ProtectionGroupAttributes: attributes, } - _, f.err = f.service.GetStorageProtectionGroupStatus(*ctx, req) + _, f.err = f.service.GetStorageProtectionGroupStatus(ctx, req) return nil } @@ -4547,12 +4601,12 @@ func (f *feature) iCallDeleteVolume(name string) error { for name, id := range volumeNameToID { fmt.Printf("volNameToID name %s id %s\n", name, id) } - ctx := new(context.Context) + ctx := context.Background() req := f.getControllerDeleteVolumeRequest("single-writer") id := arrayID + "-" + volumeNameToID[name] log.Printf("iCallDeleteVolume name %s to ID %s", name, id) req.VolumeId = id - f.deleteVolumeResponse, f.err = f.service.DeleteVolume(*ctx, req) + f.deleteVolumeResponse, f.err = f.service.DeleteVolume(ctx, req) if f.err != nil { fmt.Printf("DeleteVolume error: %s", f.err) } @@ -4560,19 +4614,19 @@ func (f *feature) iCallDeleteVolume(name string) error { } func (f *feature) iCallDeleteStorageProtectionGroup() error { - ctx := new(context.Context) + ctx := context.Background() attributes := make(map[string]string) attributes[f.service.opts.replicationContextPrefix+"systemName"] = arrayID req := &replication.DeleteStorageProtectionGroupRequest{ ProtectionGroupId: f.createStorageProtectionGroupResponse.LocalProtectionGroupId, ProtectionGroupAttributes: attributes, } - f.deleteStorageProtectionGroupResponse, f.err = f.service.DeleteStorageProtectionGroup(*ctx, req) + f.deleteStorageProtectionGroupResponse, f.err = f.service.DeleteStorageProtectionGroup(ctx, req) return nil } func (f *feature) iCallExecuteAction(arg1 string) error { - ctx := new(context.Context) + ctx := context.Background() attributes := make(map[string]string) remoteAttributes := make(map[string]string) @@ -4611,7 +4665,7 @@ func (f *feature) iCallExecuteAction(arg1 string) error { ActionTypes: &action, } - _, f.err = f.service.ExecuteAction(*ctx, req) + _, f.err = f.service.ExecuteAction(ctx, req) return nil } @@ -4744,7 +4798,7 @@ func getZoneEnabledRequest(zoneLabelName string) *csi.CreateVolumeRequest { } func (f *feature) iCallCreateVolumeWithZones(name string) error { - ctx := new(context.Context) + ctx := context.Background() if f.createVolumeRequest == nil { req := getZoneEnabledRequest(f.service.opts.zoneLabelKey) f.createVolumeRequest = req @@ -4754,7 +4808,7 @@ func (f *feature) iCallCreateVolumeWithZones(name string) error { fmt.Printf("I am in iCallCreateVolume with zones fn with req => ..... %v ...", req) - f.createVolumeResponse, f.err = f.service.CreateVolume(*ctx, req) + f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { log.Printf("CreateVolume with zones called failed: %s\n", f.err.Error()) } @@ -4771,12 +4825,12 @@ func mockGetNodeLabelsWithZone(_ context.Context, s *service) (map[string]string } func (f *feature) iCallNodeGetInfoWithZoneLabels() error { - ctx := new(context.Context) + ctx := context.Background() req := new(csi.NodeGetInfoRequest) f.service.opts.SdcGUID = "9E56672F-2F4B-4A42-BFF4-88B6846FBFDA" GetNodeLabels = mockGetNodeLabelsWithZone GetNodeUID = mockGetNodeUID - f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(*ctx, req) + f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(ctx, req) return nil } @@ -4890,6 +4944,7 @@ func FeatureContext(s *godog.ScenarioContext) { s.Step(`^a valid DeleteVolumeResponse is returned$`, f.aValidDeleteVolumeResponseIsReturned) s.Step(`^the volume is already mapped to an SDC$`, f.theVolumeIsAlreadyMappedToAnSDC) s.Step(`^I call GetCapacity with storage pool "([^"]*)"$`, f.iCallGetCapacityWithStoragePool) + s.Step(`^I call GetCapacity with Availability Zone "([^"]*)" "([^"]*)"$`, f.iCallGetCapacityWithAvailabilityZone) s.Step(`^a valid GetCapacityResponse is returned$`, f.aValidGetCapacityResponseIsReturned) s.Step(`^a valid GetCapacityResponse1 is returned$`, f.aValidGetCapacityResponsewithmaxvolsizeIsReturned) s.Step(`^I call get GetMaximumVolumeSize with systemid "([^"]*)"$`, f.iCallGetMaximumVolumeSize) @@ -5041,6 +5096,7 @@ func FeatureContext(s *godog.ScenarioContext) { s.Step(`^a valid NodeGetInfo is returned with node topology$`, f.aValidNodeGetInfoIsReturnedWithNodeTopology) s.Step(`^a NodeGetInfo is returned without zone topology$`, f.aNodeGetInfoIsReturnedWithoutZoneTopology) s.Step(`^a NodeGetInfo is returned without zone system topology$`, f.aNodeGetInfoIsReturnedWithoutZoneSystemTopology) + s.Step(`^I call systemProbeAll in mode "([^"]*)"`, f.iCallSystemProbeAll) s.After(func(ctx context.Context, _ *godog.Scenario, _ error) (context.Context, error) { if f.server != nil {