From c2d23723d93e03f06a3c8119cf80c18b0feb2bb9 Mon Sep 17 00:00:00 2001 From: jharrod Date: Thu, 5 Dec 2024 13:21:15 -0700 Subject: [PATCH] Devices utils refactor Refactor device utils into its own pacakge --- core/orchestrator_core.go | 4 +- .../controller_helpers/kubernetes/import.go | 3 +- frontend/csi/node_server.go | 75 +- frontend/csi/node_server_test.go | 169 +-- frontend/csi/plugin.go | 21 +- frontend/csi/utils.go | 7 +- frontend/csi/utils_test.go | 42 +- mocks/mock_csiutils/mock_csiutils.go | 171 +++ .../mock_ontap/mock_awsapi.go | 169 ++- .../mock_devices/mock_devices_client.go | 371 ++++++ .../mock_devices/mock_luks/mock_luks.go | 141 ++ mocks/mock_utils/mock_exec/mock_error.go | 27 + mocks/mock_utils/mock_fcp/mock_fcp_client.go | 16 + .../mock_fcp/mock_reconcile_utils.go | 45 - .../mock_filesystem/mock_filesystem_client.go | 15 - .../mock_iscsi/mock_iscsi_client.go | 30 + .../mock_iscsi/mock_iscsi_devices_client.go | 378 ------ .../mock_iscsi/mock_reconcile_utils.go | 45 - .../mock_models/mock_luks/mock_luks.go | 198 --- storage_drivers/ontap/ontap_common.go | 7 +- storage_drivers/ontap/ontap_san.go | 5 +- storage_drivers/ontap/ontap_san_economy.go | 5 +- .../ontap/ontap_san_economy_test.go | 2 +- storage_drivers/ontap/ontap_san_nvme.go | 3 +- storage_drivers/ontap/ontap_san_test.go | 2 +- storage_drivers/solidfire/solidfire_san.go | 2 +- utils/adaptors.go | 136 +- utils/devices.go | 1165 ----------------- utils/devices/devices.go | 825 ++++++++++++ utils/devices/devices_darwin.go | 70 + utils/{ => devices}/devices_darwin_test.go | 10 +- utils/devices/devices_linux.go | 303 +++++ utils/devices/devices_linux_test.go | 145 ++ utils/devices/devices_test.go | 449 +++++++ utils/devices/devices_windows.go | 70 + utils/{ => devices}/devices_windows_test.go | 12 +- utils/devices/luks/luks.go | 181 +++ utils/devices/luks/luks_darwin.go | 65 + .../luks/luks_linux.go} | 425 ++---- utils/devices/luks/luks_linux_test.go | 751 +++++++++++ utils/devices/luks/luks_test.go | 16 + utils/devices/luks/luks_windows.go | 73 ++ utils/devices/utils.go | 14 + utils/devices_darwin.go | 133 -- utils/devices_linux_test.go | 816 ------------ utils/devices_test.go | 352 ----- utils/devices_windows.go | 133 -- utils/exec/command.go | 7 + utils/fcp.go | 2 +- utils/fcp/expose.go | 31 - utils/fcp/fcp.go | 225 +--- utils/fcp/reconcile_utils.go | 87 -- utils/filesystem/filesystem.go | 10 +- utils/filesystem/filesystem_darwin.go | 8 - utils/filesystem/filesystem_darwin_test.go | 8 - utils/filesystem/filesystem_linux.go | 19 - utils/filesystem/filesystem_linux_test.go | 21 - utils/filesystem/filesystem_windows.go | 8 - utils/filesystem/filesystem_windows_test.go | 9 - utils/iscsi.go | 47 +- utils/iscsi/expose.go | 24 - utils/iscsi/iscsi.go | 685 ++++++---- utils/iscsi/iscsi_linux_test.go | 272 ++++ utils/iscsi/iscsi_test.go | 1102 +++++++--------- utils/iscsi/reconcile_utils.go | 87 -- utils/models/types.go | 19 - utils/nvme.go | 12 +- utils/utils.go | 3 - 68 files changed, 5397 insertions(+), 5386 deletions(-) create mode 100644 mocks/mock_csiutils/mock_csiutils.go create mode 100644 mocks/mock_utils/mock_devices/mock_devices_client.go create mode 100644 mocks/mock_utils/mock_devices/mock_luks/mock_luks.go create mode 100644 mocks/mock_utils/mock_exec/mock_error.go delete mode 100644 mocks/mock_utils/mock_iscsi/mock_iscsi_devices_client.go delete mode 100644 mocks/mock_utils/mock_models/mock_luks/mock_luks.go delete mode 100644 utils/devices.go create mode 100644 utils/devices/devices.go create mode 100644 utils/devices/devices_darwin.go rename utils/{ => devices}/devices_darwin_test.go (65%) create mode 100644 utils/devices/devices_linux.go create mode 100644 utils/devices/devices_linux_test.go create mode 100644 utils/devices/devices_test.go create mode 100644 utils/devices/devices_windows.go rename utils/{ => devices}/devices_windows_test.go (60%) create mode 100644 utils/devices/luks/luks.go create mode 100644 utils/devices/luks/luks_darwin.go rename utils/{devices_linux.go => devices/luks/luks_linux.go} (57%) create mode 100644 utils/devices/luks/luks_linux_test.go create mode 100644 utils/devices/luks/luks_test.go create mode 100644 utils/devices/luks/luks_windows.go create mode 100644 utils/devices/utils.go delete mode 100644 utils/devices_darwin.go delete mode 100644 utils/devices_linux_test.go delete mode 100644 utils/devices_test.go delete mode 100644 utils/devices_windows.go delete mode 100644 utils/fcp/expose.go create mode 100644 utils/iscsi/iscsi_linux_test.go diff --git a/core/orchestrator_core.go b/core/orchestrator_core.go index 97e126ad1..7c52344bc 100644 --- a/core/orchestrator_core.go +++ b/core/orchestrator_core.go @@ -111,7 +111,7 @@ type TridentOrchestrator struct { func NewTridentOrchestrator(client persistentstore.Client) (*TridentOrchestrator, error) { // TODO (vivintw) the adaptors are being plugged in here as a temporary measure to prevent cyclic dependencies. // NewClient() must plugin default implementation of the various package clients. - iscsiClient, err := iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + iscsiClient, err := iscsi.New(utils.NewOSClient()) if err != nil { return nil, err } @@ -121,7 +121,7 @@ func NewTridentOrchestrator(client persistentstore.Client) (*TridentOrchestrator return nil, err } - fcpClent, err := fcp.New(utils.NewOSClient(), utils.NewDevicesClient(), filesystem.New(mountClient)) + fcpClent, err := fcp.New(utils.NewOSClient(), filesystem.New(mountClient)) if err != nil { return nil, err } diff --git a/frontend/csi/controller_helpers/kubernetes/import.go b/frontend/csi/controller_helpers/kubernetes/import.go index a2191e80b..e628ac434 100644 --- a/frontend/csi/controller_helpers/kubernetes/import.go +++ b/frontend/csi/controller_helpers/kubernetes/import.go @@ -20,6 +20,7 @@ import ( . "github.com/netapp/trident/logging" "github.com/netapp/trident/storage" "github.com/netapp/trident/utils" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" ) @@ -115,7 +116,7 @@ func (h *helper) ImportVolume( // LUKS annotation should be accepted as either "LUKSEncryption" or "luksEncryption" to match storage pools. if luksAnnotation := getCaseFoldedAnnotation(claim.GetObjectMeta().GetAnnotations(), AnnLUKSEncryption); luksAnnotation != "" { if utils.ParseBool(luksAnnotation) { - dataSize -= utils.LUKSMetadataSize + dataSize -= luks.LUKSMetadataSize } } diff --git a/frontend/csi/node_server.go b/frontend/csi/node_server.go index a1d2d8dac..bc9c759b8 100644 --- a/frontend/csi/node_server.go +++ b/frontend/csi/node_server.go @@ -9,6 +9,7 @@ import ( "math/big" "os" "path" + "path/filepath" "runtime/debug" "strconv" "strings" @@ -25,6 +26,8 @@ import ( . "github.com/netapp/trident/logging" sa "github.com/netapp/trident/storage_attribute" "github.com/netapp/trident/utils" + "github.com/netapp/trident/utils/devices" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/fcp" "github.com/netapp/trident/utils/filesystem" @@ -499,8 +502,8 @@ func (p *Plugin) nodeExpandVolume( devicePath := publishInfo.DevicePath if utils.ParseBool(publishInfo.LUKSEncryption) { - if !utils.IsLegacyLUKSDevicePath(devicePath) { - devicePath, err = utils.GetLUKSDeviceForMultipathDevice(devicePath) + if !luks.IsLegacyLUKSDevicePath(devicePath) { + devicePath, err = p.devices.GetLUKSDeviceForMultipathDevice(devicePath) if err != nil { Logc(ctx).WithFields(LogFields{ "volumeId": volumeId, @@ -520,7 +523,8 @@ func (p *Plugin) nodeExpandVolume( return status.Error(codes.InvalidArgument, "cannot expand LUKS encrypted volume; empty passphrase provided") } - if err := utils.ResizeLUKSDevice(ctx, devicePath, passphrase); err != nil { + luksDevice := luks.NewLUKSDevice("", filepath.Base(devicePath), p.command) + if err := luksDevice.Resize(ctx, passphrase); err != nil { if errors.IsIncorrectLUKSPassphraseError(err) { return status.Error(codes.InvalidArgument, err.Error()) } @@ -1174,9 +1178,9 @@ func (p *Plugin) nodeStageFCPVolume( } if isLUKS { - var luksDevice models.LUKSDeviceInterface - luksDevice, err = utils.NewLUKSDeviceFromMappingPath( - ctx, publishInfo.DevicePath, + var luksDevice luks.Device + luksDevice, err = luks.NewLUKSDeviceFromMappingPath( + ctx, p.command, publishInfo.DevicePath, req.VolumeContext["internalName"], ) if err != nil { @@ -1231,7 +1235,8 @@ func (p *Plugin) ensureAttachFCPVolume( func (p *Plugin) nodeUnstageFCPVolume( ctx context.Context, req *csi.NodeUnstageVolumeRequest, publishInfo *models.VolumePublishInfo, force bool, ) error { - deviceInfo, err := utils.GetDeviceInfoForFCPLUN(ctx, nil, int(publishInfo.FCPLunNumber), publishInfo.FCTargetWWNN, false) + deviceInfo, err := utils.GetDeviceInfoForFCPLUN(ctx, nil, int(publishInfo.FCPLunNumber), publishInfo.FCTargetWWNN, + false) if err != nil { return fmt.Errorf("could not get device info: %v", err) } @@ -1246,14 +1251,14 @@ func (p *Plugin) nodeUnstageFCPVolume( fields := LogFields{"luksDevicePath": publishInfo.DevicePath, "lunID": publishInfo.FCPLunNumber} // Before closing the LUKS device, get the underlying mapper device from cryptsetup. - mapperPath, err := utils.GetUnderlyingDevicePathForLUKSDevice(ctx, publishInfo.DevicePath) + mapperPath, err := luks.GetUnderlyingDevicePathForLUKSDevice(ctx, p.command, publishInfo.DevicePath) if err != nil { // No need to return an error Logc(ctx).WithFields(fields).WithError(err).Error("Could not determine underlying device for LUKS.") } fields["mapperPath"] = mapperPath - err = utils.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, publishInfo.DevicePath) + err = p.devices.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, publishInfo.DevicePath) if err != nil { if errors.IsMaxWaitExceededError(err) { Logc(ctx).WithFields(LogFields{ @@ -1278,11 +1283,13 @@ func (p *Plugin) nodeUnstageFCPVolume( } // Delete the device from the host. - unmappedMpathDevice, err := utils.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, nil, p.unsafeDetach, force) + unmappedMpathDevice, err := p.iscsi.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, nil, p.unsafeDetach, + force) if err != nil { if errors.IsFCPSameLunNumberError(err) { // There is a need to pass all the publish infos this time - unmappedMpathDevice, err = utils.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, p.readAllTrackingFiles(ctx), + unmappedMpathDevice, err = p.iscsi.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, + p.readAllTrackingFiles(ctx), p.unsafeDetach, force) } @@ -1312,17 +1319,17 @@ func (p *Plugin) nodeUnstageFCPVolume( // It needs to be removed prior to removing the 'unmappedMpathDevice' device below. if luksMapperPath != "" { // EnsureLUKSDeviceClosed will not return an error if the device is already closed or removed. - if err = utils.EnsureLUKSDeviceClosed(ctx, luksMapperPath); err != nil { + if err = p.devices.EnsureLUKSDeviceClosed(ctx, luksMapperPath); err != nil { Logc(ctx).WithFields(LogFields{ "devicePath": luksMapperPath, }).WithError(err).Warning("Unable to remove LUKS mapper device.") } // Clear the time duration for the LUKS device. - delete(utils.LuksCloseDurations, luksMapperPath) + delete(devices.LuksCloseDurations, luksMapperPath) } // If there is multipath device, flush(remove) mappings - if err := utils.RemoveMultipathDeviceMapping(ctx, unmappedMpathDevice); err != nil { + if err := p.devices.RemoveMultipathDeviceMapping(ctx, unmappedMpathDevice); err != nil { return err } @@ -1384,7 +1391,7 @@ func (p *Plugin) nodePublishFCPVolume( if utils.ParseBool(publishInfo.LUKSEncryption) { // Rotate the LUKS passphrase if needed, on failure, log and continue to publish - luksDevice, err := utils.NewLUKSDeviceFromMappingPath(ctx, publishInfo.DevicePath, + luksDevice, err := luks.NewLUKSDeviceFromMappingPath(ctx, p.command, publishInfo.DevicePath, req.VolumeContext["internalName"]) if err != nil { return nil, status.Error(codes.Internal, err.Error()) @@ -1540,11 +1547,8 @@ func (p *Plugin) nodeStageISCSIVolume( return err } - var luksDevice models.LUKSDeviceInterface - luksDevice, err = p.devices.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"]) - if err != nil { - return err - } + var luksDevice luks.Device + luksDevice = luks.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"], p.command) // Ensure we update the passphrase in case it has never been set before err = ensureLUKSVolumePassphrase(ctx, p.restClient, luksDevice, volumeId, req.GetSecrets(), true) @@ -1634,7 +1638,7 @@ func (p *Plugin) nodeUnstageISCSIVolume( return nil } - deviceInfo, err := p.devices.GetDeviceInfoForLUN(ctx, hostSessionMap, int(publishInfo.IscsiLunNumber), + deviceInfo, err := p.iscsi.GetDeviceInfoForLUN(ctx, hostSessionMap, int(publishInfo.IscsiLunNumber), publishInfo.IscsiTargetIQN, false) if err != nil { Logc(ctx).WithError(err).Debug("Could not find devices.") @@ -1679,17 +1683,18 @@ func (p *Plugin) nodeUnstageISCSIVolume( } // Set device path to dm device to correctly verify legacy volumes. - if utils.IsLegacyLUKSDevicePath(publishInfo.DevicePath) { + if luks.IsLegacyLUKSDevicePath(publishInfo.DevicePath) { publishInfo.DevicePath = deviceInfo.MultipathDevice } } // Delete the device from the host. - unmappedMpathDevice, err := p.devices.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, nil, p.unsafeDetach, force) + unmappedMpathDevice, err := p.iscsi.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, nil, p.unsafeDetach, + force) if err != nil { if errors.IsISCSISameLunNumberError(err) { // There is a need to pass all the publish infos this time - unmappedMpathDevice, err = p.devices.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, + unmappedMpathDevice, err = p.iscsi.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, p.readAllTrackingFiles(ctx), p.unsafeDetach, force) } @@ -1768,7 +1773,7 @@ func (p *Plugin) nodeUnstageISCSIVolume( }).WithError(err).Warning("Unable to remove LUKS mapper device.") } // Clear the time duration for the LUKS device. - utils.LuksCloseDurations.RemoveDurationTracking(luksMapperPath) + devices.LuksCloseDurations.RemoveDurationTracking(luksMapperPath) } // If there is multipath device, flush(remove) mappings @@ -1834,14 +1839,14 @@ func (p *Plugin) nodePublishISCSIVolume( devicePath := publishInfo.DevicePath if utils.ParseBool(publishInfo.LUKSEncryption) { // Rotate the LUKS passphrase if needed, on failure, log and continue to publish - var luksDevice models.LUKSDeviceInterface + var luksDevice luks.Device var err error - if utils.IsLegacyLUKSDevicePath(devicePath) { + if luks.IsLegacyLUKSDevicePath(devicePath) { // Supports legacy volumes that store the LUKS device path - luksDevice, err = p.devices.NewLUKSDeviceFromMappingPath(ctx, devicePath, + luksDevice, err = luks.NewLUKSDeviceFromMappingPath(ctx, p.command, devicePath, req.VolumeContext["internalName"]) } else { - luksDevice, err = p.devices.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"]) + luksDevice = luks.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"], p.command) } if err != nil { @@ -2469,10 +2474,7 @@ func (p *Plugin) nodeStageNVMeVolume( } if isLUKS { - luksDevice, err := p.devices.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"]) - if err != nil { - return err - } + luksDevice := luks.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"], p.command) // Ensure we update the passphrase in case it has never been set before err = ensureLUKSVolumePassphrase(ctx, p.restClient, luksDevice, volumeId, req.GetSecrets(), true) @@ -2620,7 +2622,7 @@ func (p *Plugin) nodeUnstageNVMeVolume( }).WithError(err).Warning("Unable to remove LUKS mapper device.") } // Clear the time duration for the LUKS device. - utils.LuksCloseDurations.RemoveDurationTracking(luksMapperPath) + devices.LuksCloseDurations.RemoveDurationTracking(luksMapperPath) } // Delete the device info we saved to the volume tracking info path so unstage can succeed. @@ -2654,10 +2656,7 @@ func (p *Plugin) nodePublishNVMeVolume( devicePath := publishInfo.DevicePath if utils.ParseBool(publishInfo.LUKSEncryption) { // Rotate the LUKS passphrase if needed, on failure, log and continue to publish - luksDevice, err := p.devices.NewLUKSDevice(devicePath, req.VolumeContext["internalName"]) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } + luksDevice := luks.NewLUKSDevice(devicePath, req.VolumeContext["internalName"], p.command) err = ensureLUKSVolumePassphrase(ctx, p.restClient, luksDevice, req.GetVolumeId(), req.GetSecrets(), false) if err != nil { diff --git a/frontend/csi/node_server_test.go b/frontend/csi/node_server_test.go index cbbd6025d..2818714e0 100644 --- a/frontend/csi/node_server_test.go +++ b/frontend/csi/node_server_test.go @@ -21,11 +21,13 @@ import ( mockControllerAPI "github.com/netapp/trident/mocks/mock_frontend/mock_csi/mock_controller_api" mockNodeHelpers "github.com/netapp/trident/mocks/mock_frontend/mock_csi/mock_node_helpers" mockUtils "github.com/netapp/trident/mocks/mock_utils" + "github.com/netapp/trident/mocks/mock_utils/mock_devices" "github.com/netapp/trident/mocks/mock_utils/mock_iscsi" "github.com/netapp/trident/mocks/mock_utils/mock_mount" sa "github.com/netapp/trident/storage_attribute" "github.com/netapp/trident/utils" "github.com/netapp/trident/utils/crypto" + "github.com/netapp/trident/utils/devices" "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/filesystem" "github.com/netapp/trident/utils/iscsi" @@ -1023,7 +1025,7 @@ func TestFixISCSISessions(t *testing.T) { }, } - iscsiClient, err := iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + iscsiClient, err := iscsi.New(utils.NewOSClient()) assert.NoError(t, err) nodeServer := &Plugin{ @@ -1940,7 +1942,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { type parameters struct { getISCSIClient func() iscsi.ISCSI getIscsiReconcileUtilsClient func() iscsi.IscsiReconcileUtils - getDeviceClient func() iscsi.Devices + getDeviceClient func() devices.Devices getMountClient func() mount.Mount getNodeHelper func() nodehelpers.NodeHelper publishInfo models.VolumePublishInfo @@ -1964,14 +1966,14 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().SafeToLogOut(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) mockISCSIClient.EXPECT().RemovePortalsFromSession(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().Logout(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().RemoveMultipathDeviceMapping(gomock.Any(), gomock.Any()).Return(nil) return mockDeviceClient }, @@ -2003,16 +2005,16 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().SafeToLogOut(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) mockISCSIClient.EXPECT().RemovePortalsFromSession(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().Logout(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath).Return(nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - false, false).Return("", nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) mockDeviceClient.EXPECT().RemoveMultipathDeviceMapping(gomock.Any(), gomock.Any()).Return(nil) return mockDeviceClient @@ -2047,18 +2049,18 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().RemovePortalsFromSession(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().Logout(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) mockDeviceClient.EXPECT().RemoveMultipathDeviceMapping(gomock.Any(), gomock.Any()).Return(nil) return mockDeviceClient @@ -2087,6 +2089,8 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getISCSIClient: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) mockISCSIClient.EXPECT().RemoveLUNFromSessions(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, getIscsiReconcileUtilsClient: func() iscsi.IscsiReconcileUtils { @@ -2095,10 +2099,9 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { gomock.Any()).Return(map[int]int{6: 3}) return mockIscsiReconcileUtilsClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(fmt.Errorf("mock error")) @@ -2126,10 +2129,14 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { gomock.Any()).Return(map[int]int{6: 3}) return mockIscsiReconcileUtilsClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + getISCSIClient: func() iscsi.ISCSI { + mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), false).Return(nil, nil) + return mockISCSIClient + }, + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) return mockDeviceClient }, }, @@ -2143,10 +2150,14 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { gomock.Any()).Return(map[int]int{6: 3}) return mockIscsiReconcileUtilsClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + getISCSIClient: func() iscsi.ISCSI { + mockIscsiClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) + mockIscsiClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), false).Return(nil, fmt.Errorf("mock error")) + return mockIscsiClient + }, + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) return mockDeviceClient }, }, @@ -2157,6 +2168,8 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getISCSIClient: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) mockISCSIClient.EXPECT().RemoveLUNFromSessions(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, getIscsiReconcileUtilsClient: func() iscsi.IscsiReconcileUtils { @@ -2165,10 +2178,8 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { gomock.Any()).Return(map[int]int{6: 3}) return mockIscsiReconcileUtilsClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", fmt.Errorf( "mock error")) return mockDeviceClient @@ -2181,17 +2192,17 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getISCSIClient: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) mockISCSIClient.EXPECT().RemoveLUNFromSessions(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", fmt.Errorf("mock error")) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", fmt.Errorf("mock error")) return mockDeviceClient }, getIscsiReconcileUtilsClient: func() iscsi.IscsiReconcileUtils { @@ -2212,19 +2223,19 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().SafeToLogOut(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) mockISCSIClient.EXPECT().RemovePortalsFromSession(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().Logout(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", errors.ISCSISameLunNumberError("mock error")) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", errors.ISCSISameLunNumberError("mock error")) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) mockDeviceClient.EXPECT().RemoveMultipathDeviceMapping(gomock.Any(), gomock.Any()).Return(nil) return mockDeviceClient @@ -2242,6 +2253,8 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { }, getNodeHelper: func() nodehelpers.NodeHelper { mockNodeHelper := mockNodeHelpers.NewMockNodeHelper(gomock.NewController(t)) + mockNodeHelper.EXPECT().ReadTrackingInfo(gomock.Any(), + gomock.Any()).Return(&models.VolumeTrackingInfo{}, nil).AnyTimes() mockNodeHelper.EXPECT().DeleteTrackingInfo(gomock.Any(), gomock.Any()).Return(nil) return mockNodeHelper }, @@ -2254,14 +2267,14 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) mockISCSIClient.EXPECT().RemoveLUNFromSessions(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().TargetHasMountedDevice(gomock.Any(), gomock.Any()).Return(true, nil) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().RemoveMultipathDeviceMapping(gomock.Any(), gomock.Any()).Return(nil) return mockDeviceClient }, @@ -2291,14 +2304,14 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().RemoveLUNFromSessions(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().TargetHasMountedDevice(gomock.Any(), gomock.Any()).Return(false, nil) mockISCSIClient.EXPECT().SafeToLogOut(gomock.Any(), gomock.Any(), gomock.Any()).Return(false) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().RemoveMultipathDeviceMapping(gomock.Any(), gomock.Any()).Return(nil) return mockDeviceClient }, @@ -2330,14 +2343,14 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().SafeToLogOut(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) mockISCSIClient.EXPECT().RemovePortalsFromSession(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().Logout(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) return mockDeviceClient }, getMountClient: func() mount.Mount { @@ -2364,14 +2377,14 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().SafeToLogOut(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) mockISCSIClient.EXPECT().RemovePortalsFromSession(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().Logout(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().RemoveMultipathDeviceMapping(gomock.Any(), gomock.Any()). Return(fmt.Errorf("mock error")) return mockDeviceClient @@ -2399,14 +2412,14 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().SafeToLogOut(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) mockISCSIClient.EXPECT().RemovePortalsFromSession(gomock.Any(), gomock.Any(), gomock.Any()) mockISCSIClient.EXPECT().Logout(gomock.Any(), gomock.Any(), gomock.Any()) + mockISCSIClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), false, false).Return("", nil) + mockISCSIClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) return mockISCSIClient }, - getDeviceClient: func() iscsi.Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetDeviceInfoForLUN(gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), false).Return(mockDevice, nil) - mockDeviceClient.EXPECT().PrepareDeviceForRemoval(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), false, false).Return("", nil) + getDeviceClient: func() devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().RemoveMultipathDeviceMapping(gomock.Any(), gomock.Any()).Return(nil) return mockDeviceClient }, diff --git a/frontend/csi/plugin.go b/frontend/csi/plugin.go index ad8687a01..c1dadbe85 100644 --- a/frontend/csi/plugin.go +++ b/frontend/csi/plugin.go @@ -22,7 +22,9 @@ import ( nodehelpers "github.com/netapp/trident/frontend/csi/node_helpers" . "github.com/netapp/trident/logging" "github.com/netapp/trident/utils" + "github.com/netapp/trident/utils/devices" "github.com/netapp/trident/utils/errors" + execCmd "github.com/netapp/trident/utils/exec" "github.com/netapp/trident/utils/fcp" "github.com/netapp/trident/utils/filesystem" "github.com/netapp/trident/utils/iscsi" @@ -50,6 +52,8 @@ type Plugin struct { hostInfo *models.HostSystem + command execCmd.Command + restClient controllerAPI.TridentController controllerHelper controllerhelpers.ControllerHelper nodeHelper nodehelpers.NodeHelper @@ -83,7 +87,7 @@ type Plugin struct { nvmeSelfHealingInterval time.Duration iscsi iscsi.ISCSI - devices iscsi.Devices // TODO: this interface will be replaced once the device package is created + devices devices.Devices mount mount.Mount fs filesystem.Filesystem fcp fcp.FCP @@ -107,6 +111,7 @@ func NewControllerPlugin( controllerHelper: *helper, enableForceDetach: enableForceDetach, opCache: sync.Map{}, + command: execCmd.NewCommand(), } var err error @@ -161,7 +166,7 @@ func NewNodePlugin( // TODO (vivintw) the adaptors are being plugged in here as a temporary measure to prevent cyclic dependencies. // NewClient() must plugin default implementation of the various package clients. - iscsiClient, err := iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + iscsiClient, err := iscsi.New(utils.NewOSClient()) if err != nil { return nil, err } @@ -173,7 +178,7 @@ func NewNodePlugin( fs := filesystem.New(mountClient) - fcpClient, err := fcp.New(utils.NewOSClient(), utils.NewDevicesClient(), fs) + fcpClient, err := fcp.New(utils.NewOSClient(), fs) if err != nil { return nil, err } @@ -196,9 +201,10 @@ func NewNodePlugin( iscsi: iscsiClient, // NewClient() must plugin default implementation of the various package clients. fcp: fcpClient, - devices: utils.NewDevicesClient(), + devices: devices.New(), mount: mountClient, fs: fs, + command: execCmd.NewCommand(), } if runtime.GOOS == "windows" { @@ -269,7 +275,7 @@ func NewAllInOnePlugin( // TODO (vivintw) the adaptors are being plugged in here as a temporary measure to prevent cyclic dependencies. // NewClient() must plugin default implementation of the various package clients. - iscsiClient, err := iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + iscsiClient, err := iscsi.New(utils.NewOSClient()) if err != nil { return nil, err } @@ -281,7 +287,7 @@ func NewAllInOnePlugin( fs := filesystem.New(mountClient) - fcpClient, err := fcp.New(utils.NewOSClient(), utils.NewDevicesClient(), fs) + fcpClient, err := fcp.New(utils.NewOSClient(), fs) if err != nil { return nil, err } @@ -303,9 +309,10 @@ func NewAllInOnePlugin( nvmeSelfHealingInterval: nvmeSelfHealingInterval, iscsi: iscsiClient, fcp: fcpClient, - devices: utils.NewDevicesClient(), + devices: devices.New(), mount: mountClient, fs: fs, + command: execCmd.NewCommand(), } // Define controller capabilities diff --git a/frontend/csi/utils.go b/frontend/csi/utils.go index 6221809fd..d47607f0e 100644 --- a/frontend/csi/utils.go +++ b/frontend/csi/utils.go @@ -16,8 +16,8 @@ import ( controllerAPI "github.com/netapp/trident/frontend/csi/controller_api" . "github.com/netapp/trident/logging" sa "github.com/netapp/trident/storage_attribute" - "github.com/netapp/trident/utils" "github.com/netapp/trident/utils/crypto" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/models" ) @@ -246,10 +246,11 @@ func performProtocolSpecificReconciliation(ctx context.Context, trackingInfo *mo // of any possibly in use passphrases. If forceUpdate is true, the Trident controller will be notified of the current // passphrase name, regardless of a rotation. func ensureLUKSVolumePassphrase( - ctx context.Context, restClient controllerAPI.TridentController, luksDevice models.LUKSDeviceInterface, + ctx context.Context, restClient controllerAPI.TridentController, luksDevice luks.Device, volumeId string, secrets map[string]string, forceUpdate bool, ) error { - luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase := utils.GetLUKSPassphrasesFromSecretMap(secrets) + luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, + previousLUKSPassphrase := luks.GetLUKSPassphrasesFromSecretMap(secrets) if luksPassphrase == "" { return fmt.Errorf("LUKS passphrase cannot be empty") } diff --git a/frontend/csi/utils_test.go b/frontend/csi/utils_test.go index 7e2ed6f40..a73225439 100644 --- a/frontend/csi/utils_test.go +++ b/frontend/csi/utils_test.go @@ -12,8 +12,8 @@ import ( "github.com/netapp/trident/config" mockControllerAPI "github.com/netapp/trident/mocks/mock_frontend/mock_csi/mock_controller_api" + "github.com/netapp/trident/mocks/mock_utils/mock_devices/mock_luks" "github.com/netapp/trident/mocks/mock_utils/mock_iscsi" - "github.com/netapp/trident/mocks/mock_utils/mock_models/mock_luks" sa "github.com/netapp/trident/storage_attribute" "github.com/netapp/trident/utils" "github.com/netapp/trident/utils/errors" @@ -113,7 +113,7 @@ func TestEnsureLUKSVolumePassphrase(t *testing.T) { // Positive case: Passphrase already latest mockCtrl := gomock.NewController(t) mockClient := mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice := mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice := mock_luks.NewMockDevice(mockCtrl) secrets := map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "passphraseA", @@ -127,7 +127,7 @@ func TestEnsureLUKSVolumePassphrase(t *testing.T) { // Positive case: Passphrase already latest, force update mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "passphraseA", @@ -142,7 +142,7 @@ func TestEnsureLUKSVolumePassphrase(t *testing.T) { // Positive case: Passphrase not correct, but previous passphrase is correct, rotation needed mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "B", "luks-passphrase": "passphraseB", @@ -165,7 +165,7 @@ func TestEnsureLUKSVolumePassphrase_Error(t *testing.T) { // Negative case: Checking passphrase is correct errors mockCtrl := gomock.NewController(t) mockClient := mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice := mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice := mock_luks.NewMockDevice(mockCtrl) secrets := map[string]string{ "luks-passphrase-name": "B", "luks-passphrase": "passphraseB", @@ -181,7 +181,7 @@ func TestEnsureLUKSVolumePassphrase_Error(t *testing.T) { // Negative case: Checking previous passphrase is correct errors mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "B", "luks-passphrase": "passphraseB", @@ -198,7 +198,7 @@ func TestEnsureLUKSVolumePassphrase_Error(t *testing.T) { // Negative case: Sending pre-rotation passphrases to trident controller fails mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "B", "luks-passphrase": "passphraseB", @@ -216,7 +216,7 @@ func TestEnsureLUKSVolumePassphrase_Error(t *testing.T) { // Negative case: Passphrase rotation fails mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "B", "luks-passphrase": "passphraseB", @@ -235,7 +235,7 @@ func TestEnsureLUKSVolumePassphrase_Error(t *testing.T) { // Negative case: Verifying passphrase rotation fails mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "B", "luks-passphrase": "passphraseB", @@ -255,7 +255,7 @@ func TestEnsureLUKSVolumePassphrase_Error(t *testing.T) { // Negative case: Sending post-rotation passphrases to trident controller fails mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "B", "luks-passphrase": "passphraseB", @@ -278,7 +278,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: No passphrases mockCtrl := gomock.NewController(t) mockClient := mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice := mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice := mock_luks.NewMockDevice(mockCtrl) secrets := map[string]string{} err := ensureLUKSVolumePassphrase(context.TODO(), mockClient, mockLUKSDevice, "test-vol", secrets, false) assert.Error(t, err) @@ -288,7 +288,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: passphrase but no passphrase name mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase": "passphraseA", } @@ -300,7 +300,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: passphrase but empty passphrase name mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "", @@ -313,7 +313,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: passphrase name but no passphrase name mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "A", } @@ -325,7 +325,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: passphrase name but empty passphrase name mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "", @@ -338,7 +338,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: passphrase valid, previous luks passphrase valid but no name mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "passphraseA", @@ -353,7 +353,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: passphrase valid, previous luks passphrase valid but empty name mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "passphraseA", @@ -369,7 +369,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: passphrase valid, previous luks passphrase name valid but no passphrase mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "passphraseA", @@ -384,7 +384,7 @@ func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { // Negative case: passphrase valid, previous luks passphrase name valid but empty passphrase mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "passphraseA", @@ -402,7 +402,7 @@ func TestEnsureLUKSVolumePassphrase_NoCorrectPassphraseProvided(t *testing.T) { // Negative case: Passphrase not correct and no previous specified mockCtrl := gomock.NewController(t) mockClient := mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice := mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice := mock_luks.NewMockDevice(mockCtrl) secrets := map[string]string{ "luks-passphrase-name": "A", "luks-passphrase": "passphraseA", @@ -416,7 +416,7 @@ func TestEnsureLUKSVolumePassphrase_NoCorrectPassphraseProvided(t *testing.T) { // Negative case: Passphrase not correct and incorrect previous mockCtrl = gomock.NewController(t) mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) + mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) secrets = map[string]string{ "luks-passphrase-name": "B", "luks-passphrase": "passphraseB", diff --git a/mocks/mock_csiutils/mock_csiutils.go b/mocks/mock_csiutils/mock_csiutils.go new file mode 100644 index 000000000..09902af8f --- /dev/null +++ b/mocks/mock_csiutils/mock_csiutils.go @@ -0,0 +1,171 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/netapp/trident/utils/csiutils (interfaces: CSIProxyUtils) +// +// Generated by this command: +// +// mockgen -destination=../../mocks/mock_csiutils/mock_csiutils.go github.com/netapp/trident/utils/csiutils CSIProxyUtils +// + +// Package mock_csiutils is a generated GoMock package. +package mock_csiutils + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + mount "k8s.io/mount-utils" +) + +// MockCSIProxyUtils is a mock of CSIProxyUtils interface. +type MockCSIProxyUtils struct { + ctrl *gomock.Controller + recorder *MockCSIProxyUtilsMockRecorder +} + +// MockCSIProxyUtilsMockRecorder is the mock recorder for MockCSIProxyUtils. +type MockCSIProxyUtilsMockRecorder struct { + mock *MockCSIProxyUtils +} + +// NewMockCSIProxyUtils creates a new mock instance. +func NewMockCSIProxyUtils(ctrl *gomock.Controller) *MockCSIProxyUtils { + mock := &MockCSIProxyUtils{ctrl: ctrl} + mock.recorder = &MockCSIProxyUtilsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCSIProxyUtils) EXPECT() *MockCSIProxyUtilsMockRecorder { + return m.recorder +} + +// EvalHostSymlinks mocks base method. +func (m *MockCSIProxyUtils) EvalHostSymlinks(arg0 context.Context, arg1 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EvalHostSymlinks", arg0, arg1) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EvalHostSymlinks indicates an expected call of EvalHostSymlinks. +func (mr *MockCSIProxyUtilsMockRecorder) EvalHostSymlinks(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EvalHostSymlinks", reflect.TypeOf((*MockCSIProxyUtils)(nil).EvalHostSymlinks), arg0, arg1) +} + +// ExistsPath mocks base method. +func (m *MockCSIProxyUtils) ExistsPath(arg0 context.Context, arg1 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExistsPath", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExistsPath indicates an expected call of ExistsPath. +func (mr *MockCSIProxyUtilsMockRecorder) ExistsPath(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsPath", reflect.TypeOf((*MockCSIProxyUtils)(nil).ExistsPath), arg0, arg1) +} + +// GetAPIVersions mocks base method. +func (m *MockCSIProxyUtils) GetAPIVersions(arg0 context.Context) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAPIVersions", arg0) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetAPIVersions indicates an expected call of GetAPIVersions. +func (mr *MockCSIProxyUtilsMockRecorder) GetAPIVersions(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAPIVersions", reflect.TypeOf((*MockCSIProxyUtils)(nil).GetAPIVersions), arg0) +} + +// GetFilesystemUsage mocks base method. +func (m *MockCSIProxyUtils) GetFilesystemUsage(arg0 context.Context, arg1 string) (int64, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFilesystemUsage", arg0, arg1) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetFilesystemUsage indicates an expected call of GetFilesystemUsage. +func (mr *MockCSIProxyUtilsMockRecorder) GetFilesystemUsage(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFilesystemUsage", reflect.TypeOf((*MockCSIProxyUtils)(nil).GetFilesystemUsage), arg0, arg1) +} + +// IsMountPointMatch mocks base method. +func (m *MockCSIProxyUtils) IsMountPointMatch(arg0 context.Context, arg1 mount.MountPoint, arg2 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsMountPointMatch", arg0, arg1, arg2) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsMountPointMatch indicates an expected call of IsMountPointMatch. +func (mr *MockCSIProxyUtilsMockRecorder) IsMountPointMatch(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsMountPointMatch", reflect.TypeOf((*MockCSIProxyUtils)(nil).IsMountPointMatch), arg0, arg1, arg2) +} + +// MakeDir mocks base method. +func (m *MockCSIProxyUtils) MakeDir(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MakeDir", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// MakeDir indicates an expected call of MakeDir. +func (mr *MockCSIProxyUtilsMockRecorder) MakeDir(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MakeDir", reflect.TypeOf((*MockCSIProxyUtils)(nil).MakeDir), arg0, arg1) +} + +// Rmdir mocks base method. +func (m *MockCSIProxyUtils) Rmdir(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Rmdir", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Rmdir indicates an expected call of Rmdir. +func (mr *MockCSIProxyUtilsMockRecorder) Rmdir(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rmdir", reflect.TypeOf((*MockCSIProxyUtils)(nil).Rmdir), arg0, arg1) +} + +// SMBMount mocks base method. +func (m *MockCSIProxyUtils) SMBMount(arg0 context.Context, arg1, arg2, arg3, arg4, arg5 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SMBMount", arg0, arg1, arg2, arg3, arg4, arg5) + ret0, _ := ret[0].(error) + return ret0 +} + +// SMBMount indicates an expected call of SMBMount. +func (mr *MockCSIProxyUtilsMockRecorder) SMBMount(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMBMount", reflect.TypeOf((*MockCSIProxyUtils)(nil).SMBMount), arg0, arg1, arg2, arg3, arg4, arg5) +} + +// SMBUnmount mocks base method. +func (m *MockCSIProxyUtils) SMBUnmount(arg0 context.Context, arg1, arg2 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SMBUnmount", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// SMBUnmount indicates an expected call of SMBUnmount. +func (mr *MockCSIProxyUtilsMockRecorder) SMBUnmount(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMBUnmount", reflect.TypeOf((*MockCSIProxyUtils)(nil).SMBUnmount), arg0, arg1, arg2) +} diff --git a/mocks/mock_storage_drivers/mock_ontap/mock_awsapi.go b/mocks/mock_storage_drivers/mock_ontap/mock_awsapi.go index 842ddaeaf..2d69aea1d 100644 --- a/mocks/mock_storage_drivers/mock_ontap/mock_awsapi.go +++ b/mocks/mock_storage_drivers/mock_ontap/mock_awsapi.go @@ -23,7 +23,6 @@ import ( type MockAWSAPI struct { ctrl *gomock.Controller recorder *MockAWSAPIMockRecorder - isgomock struct{} } // MockAWSAPIMockRecorder is the mock recorder for MockAWSAPI. @@ -44,247 +43,247 @@ func (m *MockAWSAPI) EXPECT() *MockAWSAPIMockRecorder { } // CreateSVM mocks base method. -func (m *MockAWSAPI) CreateSVM(ctx context.Context, request *awsapi.SVMCreateRequest) (*awsapi.SVM, error) { +func (m *MockAWSAPI) CreateSVM(arg0 context.Context, arg1 *awsapi.SVMCreateRequest) (*awsapi.SVM, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSVM", ctx, request) + ret := m.ctrl.Call(m, "CreateSVM", arg0, arg1) ret0, _ := ret[0].(*awsapi.SVM) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateSVM indicates an expected call of CreateSVM. -func (mr *MockAWSAPIMockRecorder) CreateSVM(ctx, request any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) CreateSVM(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSVM", reflect.TypeOf((*MockAWSAPI)(nil).CreateSVM), ctx, request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSVM", reflect.TypeOf((*MockAWSAPI)(nil).CreateSVM), arg0, arg1) } // CreateSecret mocks base method. -func (m *MockAWSAPI) CreateSecret(ctx context.Context, request *awsapi.SecretCreateRequest) (*awsapi.Secret, error) { +func (m *MockAWSAPI) CreateSecret(arg0 context.Context, arg1 *awsapi.SecretCreateRequest) (*awsapi.Secret, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSecret", ctx, request) + ret := m.ctrl.Call(m, "CreateSecret", arg0, arg1) ret0, _ := ret[0].(*awsapi.Secret) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateSecret indicates an expected call of CreateSecret. -func (mr *MockAWSAPIMockRecorder) CreateSecret(ctx, request any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) CreateSecret(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSecret", reflect.TypeOf((*MockAWSAPI)(nil).CreateSecret), ctx, request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSecret", reflect.TypeOf((*MockAWSAPI)(nil).CreateSecret), arg0, arg1) } // CreateVolume mocks base method. -func (m *MockAWSAPI) CreateVolume(ctx context.Context, request *awsapi.VolumeCreateRequest) (*awsapi.Volume, error) { +func (m *MockAWSAPI) CreateVolume(arg0 context.Context, arg1 *awsapi.VolumeCreateRequest) (*awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateVolume", ctx, request) + ret := m.ctrl.Call(m, "CreateVolume", arg0, arg1) ret0, _ := ret[0].(*awsapi.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateVolume indicates an expected call of CreateVolume. -func (mr *MockAWSAPIMockRecorder) CreateVolume(ctx, request any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) CreateVolume(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVolume", reflect.TypeOf((*MockAWSAPI)(nil).CreateVolume), ctx, request) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVolume", reflect.TypeOf((*MockAWSAPI)(nil).CreateVolume), arg0, arg1) } // DeleteVolume mocks base method. -func (m *MockAWSAPI) DeleteVolume(ctx context.Context, volume *awsapi.Volume) error { +func (m *MockAWSAPI) DeleteVolume(arg0 context.Context, arg1 *awsapi.Volume) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteVolume", ctx, volume) + ret := m.ctrl.Call(m, "DeleteVolume", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // DeleteVolume indicates an expected call of DeleteVolume. -func (mr *MockAWSAPIMockRecorder) DeleteVolume(ctx, volume any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) DeleteVolume(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolume", reflect.TypeOf((*MockAWSAPI)(nil).DeleteVolume), ctx, volume) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolume", reflect.TypeOf((*MockAWSAPI)(nil).DeleteVolume), arg0, arg1) } // GetFilesystemByID mocks base method. -func (m *MockAWSAPI) GetFilesystemByID(ctx context.Context, ID string) (*awsapi.Filesystem, error) { +func (m *MockAWSAPI) GetFilesystemByID(arg0 context.Context, arg1 string) (*awsapi.Filesystem, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetFilesystemByID", ctx, ID) + ret := m.ctrl.Call(m, "GetFilesystemByID", arg0, arg1) ret0, _ := ret[0].(*awsapi.Filesystem) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFilesystemByID indicates an expected call of GetFilesystemByID. -func (mr *MockAWSAPIMockRecorder) GetFilesystemByID(ctx, ID any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetFilesystemByID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFilesystemByID", reflect.TypeOf((*MockAWSAPI)(nil).GetFilesystemByID), ctx, ID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFilesystemByID", reflect.TypeOf((*MockAWSAPI)(nil).GetFilesystemByID), arg0, arg1) } // GetFilesystems mocks base method. -func (m *MockAWSAPI) GetFilesystems(ctx context.Context) (*[]*awsapi.Filesystem, error) { +func (m *MockAWSAPI) GetFilesystems(arg0 context.Context) (*[]*awsapi.Filesystem, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetFilesystems", ctx) + ret := m.ctrl.Call(m, "GetFilesystems", arg0) ret0, _ := ret[0].(*[]*awsapi.Filesystem) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFilesystems indicates an expected call of GetFilesystems. -func (mr *MockAWSAPIMockRecorder) GetFilesystems(ctx any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetFilesystems(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFilesystems", reflect.TypeOf((*MockAWSAPI)(nil).GetFilesystems), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFilesystems", reflect.TypeOf((*MockAWSAPI)(nil).GetFilesystems), arg0) } // GetSVMByID mocks base method. -func (m *MockAWSAPI) GetSVMByID(ctx context.Context, ID string) (*awsapi.SVM, error) { +func (m *MockAWSAPI) GetSVMByID(arg0 context.Context, arg1 string) (*awsapi.SVM, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSVMByID", ctx, ID) + ret := m.ctrl.Call(m, "GetSVMByID", arg0, arg1) ret0, _ := ret[0].(*awsapi.SVM) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSVMByID indicates an expected call of GetSVMByID. -func (mr *MockAWSAPIMockRecorder) GetSVMByID(ctx, ID any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetSVMByID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSVMByID", reflect.TypeOf((*MockAWSAPI)(nil).GetSVMByID), ctx, ID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSVMByID", reflect.TypeOf((*MockAWSAPI)(nil).GetSVMByID), arg0, arg1) } // GetSVMs mocks base method. -func (m *MockAWSAPI) GetSVMs(ctx context.Context) (*[]*awsapi.SVM, error) { +func (m *MockAWSAPI) GetSVMs(arg0 context.Context) (*[]*awsapi.SVM, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSVMs", ctx) + ret := m.ctrl.Call(m, "GetSVMs", arg0) ret0, _ := ret[0].(*[]*awsapi.SVM) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSVMs indicates an expected call of GetSVMs. -func (mr *MockAWSAPIMockRecorder) GetSVMs(ctx any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetSVMs(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSVMs", reflect.TypeOf((*MockAWSAPI)(nil).GetSVMs), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSVMs", reflect.TypeOf((*MockAWSAPI)(nil).GetSVMs), arg0) } // GetSecret mocks base method. -func (m *MockAWSAPI) GetSecret(ctx context.Context, secretARN string) (*awsapi.Secret, error) { +func (m *MockAWSAPI) GetSecret(arg0 context.Context, arg1 string) (*awsapi.Secret, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSecret", ctx, secretARN) + ret := m.ctrl.Call(m, "GetSecret", arg0, arg1) ret0, _ := ret[0].(*awsapi.Secret) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSecret indicates an expected call of GetSecret. -func (mr *MockAWSAPIMockRecorder) GetSecret(ctx, secretARN any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetSecret(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecret", reflect.TypeOf((*MockAWSAPI)(nil).GetSecret), ctx, secretARN) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecret", reflect.TypeOf((*MockAWSAPI)(nil).GetSecret), arg0, arg1) } // GetVolume mocks base method. -func (m *MockAWSAPI) GetVolume(ctx context.Context, volConfig *storage.VolumeConfig) (*awsapi.Volume, error) { +func (m *MockAWSAPI) GetVolume(arg0 context.Context, arg1 *storage.VolumeConfig) (*awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolume", ctx, volConfig) + ret := m.ctrl.Call(m, "GetVolume", arg0, arg1) ret0, _ := ret[0].(*awsapi.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolume indicates an expected call of GetVolume. -func (mr *MockAWSAPIMockRecorder) GetVolume(ctx, volConfig any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetVolume(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolume", reflect.TypeOf((*MockAWSAPI)(nil).GetVolume), ctx, volConfig) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolume", reflect.TypeOf((*MockAWSAPI)(nil).GetVolume), arg0, arg1) } // GetVolumeByARN mocks base method. -func (m *MockAWSAPI) GetVolumeByARN(ctx context.Context, volumeARN string) (*awsapi.Volume, error) { +func (m *MockAWSAPI) GetVolumeByARN(arg0 context.Context, arg1 string) (*awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeByARN", ctx, volumeARN) + ret := m.ctrl.Call(m, "GetVolumeByARN", arg0, arg1) ret0, _ := ret[0].(*awsapi.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeByARN indicates an expected call of GetVolumeByARN. -func (mr *MockAWSAPIMockRecorder) GetVolumeByARN(ctx, volumeARN any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetVolumeByARN(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByARN", reflect.TypeOf((*MockAWSAPI)(nil).GetVolumeByARN), ctx, volumeARN) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByARN", reflect.TypeOf((*MockAWSAPI)(nil).GetVolumeByARN), arg0, arg1) } // GetVolumeByID mocks base method. -func (m *MockAWSAPI) GetVolumeByID(ctx context.Context, volumeID string) (*awsapi.Volume, error) { +func (m *MockAWSAPI) GetVolumeByID(arg0 context.Context, arg1 string) (*awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeByID", ctx, volumeID) + ret := m.ctrl.Call(m, "GetVolumeByID", arg0, arg1) ret0, _ := ret[0].(*awsapi.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeByID indicates an expected call of GetVolumeByID. -func (mr *MockAWSAPIMockRecorder) GetVolumeByID(ctx, volumeID any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetVolumeByID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByID", reflect.TypeOf((*MockAWSAPI)(nil).GetVolumeByID), ctx, volumeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByID", reflect.TypeOf((*MockAWSAPI)(nil).GetVolumeByID), arg0, arg1) } // GetVolumeByName mocks base method. -func (m *MockAWSAPI) GetVolumeByName(ctx context.Context, name string) (*awsapi.Volume, error) { +func (m *MockAWSAPI) GetVolumeByName(arg0 context.Context, arg1 string) (*awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeByName", ctx, name) + ret := m.ctrl.Call(m, "GetVolumeByName", arg0, arg1) ret0, _ := ret[0].(*awsapi.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeByName indicates an expected call of GetVolumeByName. -func (mr *MockAWSAPIMockRecorder) GetVolumeByName(ctx, name any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetVolumeByName(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByName", reflect.TypeOf((*MockAWSAPI)(nil).GetVolumeByName), ctx, name) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByName", reflect.TypeOf((*MockAWSAPI)(nil).GetVolumeByName), arg0, arg1) } // GetVolumes mocks base method. -func (m *MockAWSAPI) GetVolumes(ctx context.Context) (*[]*awsapi.Volume, error) { +func (m *MockAWSAPI) GetVolumes(arg0 context.Context) (*[]*awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumes", ctx) + ret := m.ctrl.Call(m, "GetVolumes", arg0) ret0, _ := ret[0].(*[]*awsapi.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumes indicates an expected call of GetVolumes. -func (mr *MockAWSAPIMockRecorder) GetVolumes(ctx any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) GetVolumes(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumes", reflect.TypeOf((*MockAWSAPI)(nil).GetVolumes), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumes", reflect.TypeOf((*MockAWSAPI)(nil).GetVolumes), arg0) } // RelabelVolume mocks base method. -func (m *MockAWSAPI) RelabelVolume(ctx context.Context, volume *awsapi.Volume, labels map[string]string) error { +func (m *MockAWSAPI) RelabelVolume(arg0 context.Context, arg1 *awsapi.Volume, arg2 map[string]string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RelabelVolume", ctx, volume, labels) + ret := m.ctrl.Call(m, "RelabelVolume", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // RelabelVolume indicates an expected call of RelabelVolume. -func (mr *MockAWSAPIMockRecorder) RelabelVolume(ctx, volume, labels any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) RelabelVolume(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RelabelVolume", reflect.TypeOf((*MockAWSAPI)(nil).RelabelVolume), ctx, volume, labels) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RelabelVolume", reflect.TypeOf((*MockAWSAPI)(nil).RelabelVolume), arg0, arg1, arg2) } // ResizeVolume mocks base method. -func (m *MockAWSAPI) ResizeVolume(ctx context.Context, volume *awsapi.Volume, newSizeBytes uint64) (*awsapi.Volume, error) { +func (m *MockAWSAPI) ResizeVolume(arg0 context.Context, arg1 *awsapi.Volume, arg2 uint64) (*awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ResizeVolume", ctx, volume, newSizeBytes) + ret := m.ctrl.Call(m, "ResizeVolume", arg0, arg1, arg2) ret0, _ := ret[0].(*awsapi.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // ResizeVolume indicates an expected call of ResizeVolume. -func (mr *MockAWSAPIMockRecorder) ResizeVolume(ctx, volume, newSizeBytes any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) ResizeVolume(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResizeVolume", reflect.TypeOf((*MockAWSAPI)(nil).ResizeVolume), ctx, volume, newSizeBytes) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResizeVolume", reflect.TypeOf((*MockAWSAPI)(nil).ResizeVolume), arg0, arg1, arg2) } // VolumeExists mocks base method. -func (m *MockAWSAPI) VolumeExists(ctx context.Context, volConfig *storage.VolumeConfig) (bool, *awsapi.Volume, error) { +func (m *MockAWSAPI) VolumeExists(arg0 context.Context, arg1 *storage.VolumeConfig) (bool, *awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeExists", ctx, volConfig) + ret := m.ctrl.Call(m, "VolumeExists", arg0, arg1) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(*awsapi.Volume) ret2, _ := ret[2].(error) @@ -292,15 +291,15 @@ func (m *MockAWSAPI) VolumeExists(ctx context.Context, volConfig *storage.Volume } // VolumeExists indicates an expected call of VolumeExists. -func (mr *MockAWSAPIMockRecorder) VolumeExists(ctx, volConfig any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) VolumeExists(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeExists", reflect.TypeOf((*MockAWSAPI)(nil).VolumeExists), ctx, volConfig) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeExists", reflect.TypeOf((*MockAWSAPI)(nil).VolumeExists), arg0, arg1) } // VolumeExistsByARN mocks base method. -func (m *MockAWSAPI) VolumeExistsByARN(ctx context.Context, volumeARN string) (bool, *awsapi.Volume, error) { +func (m *MockAWSAPI) VolumeExistsByARN(arg0 context.Context, arg1 string) (bool, *awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeExistsByARN", ctx, volumeARN) + ret := m.ctrl.Call(m, "VolumeExistsByARN", arg0, arg1) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(*awsapi.Volume) ret2, _ := ret[2].(error) @@ -308,15 +307,15 @@ func (m *MockAWSAPI) VolumeExistsByARN(ctx context.Context, volumeARN string) (b } // VolumeExistsByARN indicates an expected call of VolumeExistsByARN. -func (mr *MockAWSAPIMockRecorder) VolumeExistsByARN(ctx, volumeARN any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) VolumeExistsByARN(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeExistsByARN", reflect.TypeOf((*MockAWSAPI)(nil).VolumeExistsByARN), ctx, volumeARN) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeExistsByARN", reflect.TypeOf((*MockAWSAPI)(nil).VolumeExistsByARN), arg0, arg1) } // VolumeExistsByID mocks base method. -func (m *MockAWSAPI) VolumeExistsByID(ctx context.Context, volumeID string) (bool, *awsapi.Volume, error) { +func (m *MockAWSAPI) VolumeExistsByID(arg0 context.Context, arg1 string) (bool, *awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeExistsByID", ctx, volumeID) + ret := m.ctrl.Call(m, "VolumeExistsByID", arg0, arg1) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(*awsapi.Volume) ret2, _ := ret[2].(error) @@ -324,15 +323,15 @@ func (m *MockAWSAPI) VolumeExistsByID(ctx context.Context, volumeID string) (boo } // VolumeExistsByID indicates an expected call of VolumeExistsByID. -func (mr *MockAWSAPIMockRecorder) VolumeExistsByID(ctx, volumeID any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) VolumeExistsByID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeExistsByID", reflect.TypeOf((*MockAWSAPI)(nil).VolumeExistsByID), ctx, volumeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeExistsByID", reflect.TypeOf((*MockAWSAPI)(nil).VolumeExistsByID), arg0, arg1) } // VolumeExistsByName mocks base method. -func (m *MockAWSAPI) VolumeExistsByName(ctx context.Context, name string) (bool, *awsapi.Volume, error) { +func (m *MockAWSAPI) VolumeExistsByName(arg0 context.Context, arg1 string) (bool, *awsapi.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeExistsByName", ctx, name) + ret := m.ctrl.Call(m, "VolumeExistsByName", arg0, arg1) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(*awsapi.Volume) ret2, _ := ret[2].(error) @@ -340,22 +339,22 @@ func (m *MockAWSAPI) VolumeExistsByName(ctx context.Context, name string) (bool, } // VolumeExistsByName indicates an expected call of VolumeExistsByName. -func (mr *MockAWSAPIMockRecorder) VolumeExistsByName(ctx, name any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) VolumeExistsByName(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeExistsByName", reflect.TypeOf((*MockAWSAPI)(nil).VolumeExistsByName), ctx, name) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeExistsByName", reflect.TypeOf((*MockAWSAPI)(nil).VolumeExistsByName), arg0, arg1) } // WaitForVolumeStates mocks base method. -func (m *MockAWSAPI) WaitForVolumeStates(ctx context.Context, volume *awsapi.Volume, desiredStates, abortStates []string, maxElapsedTime time.Duration) (string, error) { +func (m *MockAWSAPI) WaitForVolumeStates(arg0 context.Context, arg1 *awsapi.Volume, arg2, arg3 []string, arg4 time.Duration) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitForVolumeStates", ctx, volume, desiredStates, abortStates, maxElapsedTime) + ret := m.ctrl.Call(m, "WaitForVolumeStates", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // WaitForVolumeStates indicates an expected call of WaitForVolumeStates. -func (mr *MockAWSAPIMockRecorder) WaitForVolumeStates(ctx, volume, desiredStates, abortStates, maxElapsedTime any) *gomock.Call { +func (mr *MockAWSAPIMockRecorder) WaitForVolumeStates(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForVolumeStates", reflect.TypeOf((*MockAWSAPI)(nil).WaitForVolumeStates), ctx, volume, desiredStates, abortStates, maxElapsedTime) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForVolumeStates", reflect.TypeOf((*MockAWSAPI)(nil).WaitForVolumeStates), arg0, arg1, arg2, arg3, arg4) } diff --git a/mocks/mock_utils/mock_devices/mock_devices_client.go b/mocks/mock_utils/mock_devices/mock_devices_client.go new file mode 100644 index 000000000..930714dcf --- /dev/null +++ b/mocks/mock_utils/mock_devices/mock_devices_client.go @@ -0,0 +1,371 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/netapp/trident/utils/devices (interfaces: Devices) +// +// Generated by this command: +// +// mockgen -destination=../../mocks/mock_utils/mock_devices/mock_devices_client.go github.com/netapp/trident/utils/devices Devices +// + +// Package mock_devices is a generated GoMock package. +package mock_devices + +import ( + context "context" + reflect "reflect" + time "time" + + models "github.com/netapp/trident/utils/models" + gomock "go.uber.org/mock/gomock" +) + +// MockDevices is a mock of Devices interface. +type MockDevices struct { + ctrl *gomock.Controller + recorder *MockDevicesMockRecorder +} + +// MockDevicesMockRecorder is the mock recorder for MockDevices. +type MockDevicesMockRecorder struct { + mock *MockDevices +} + +// NewMockDevices creates a new mock instance. +func NewMockDevices(ctrl *gomock.Controller) *MockDevices { + mock := &MockDevices{ctrl: ctrl} + mock.recorder = &MockDevicesMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDevices) EXPECT() *MockDevicesMockRecorder { + return m.recorder +} + +// CloseLUKSDevice mocks base method. +func (m *MockDevices) CloseLUKSDevice(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CloseLUKSDevice", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// CloseLUKSDevice indicates an expected call of CloseLUKSDevice. +func (mr *MockDevicesMockRecorder) CloseLUKSDevice(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseLUKSDevice", reflect.TypeOf((*MockDevices)(nil).CloseLUKSDevice), arg0, arg1) +} + +// EnsureDeviceReadable mocks base method. +func (m *MockDevices) EnsureDeviceReadable(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureDeviceReadable", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnsureDeviceReadable indicates an expected call of EnsureDeviceReadable. +func (mr *MockDevicesMockRecorder) EnsureDeviceReadable(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceReadable", reflect.TypeOf((*MockDevices)(nil).EnsureDeviceReadable), arg0, arg1) +} + +// EnsureLUKSDeviceClosed mocks base method. +func (m *MockDevices) EnsureLUKSDeviceClosed(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosed", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnsureLUKSDeviceClosed indicates an expected call of EnsureLUKSDeviceClosed. +func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosed(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosed", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosed), arg0, arg1) +} + +// EnsureLUKSDeviceClosedWithMaxWaitLimit mocks base method. +func (m *MockDevices) EnsureLUKSDeviceClosedWithMaxWaitLimit(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosedWithMaxWaitLimit", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnsureLUKSDeviceClosedWithMaxWaitLimit indicates an expected call of EnsureLUKSDeviceClosedWithMaxWaitLimit. +func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosedWithMaxWaitLimit(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosedWithMaxWaitLimit", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosedWithMaxWaitLimit), arg0, arg1) +} + +// FindDevicesForMultipathDevice mocks base method. +func (m *MockDevices) FindDevicesForMultipathDevice(arg0 context.Context, arg1 string) []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindDevicesForMultipathDevice", arg0, arg1) + ret0, _ := ret[0].([]string) + return ret0 +} + +// FindDevicesForMultipathDevice indicates an expected call of FindDevicesForMultipathDevice. +func (mr *MockDevicesMockRecorder) FindDevicesForMultipathDevice(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindDevicesForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).FindDevicesForMultipathDevice), arg0, arg1) +} + +// FindMultipathDeviceForDevice mocks base method. +func (m *MockDevices) FindMultipathDeviceForDevice(arg0 context.Context, arg1 string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindMultipathDeviceForDevice", arg0, arg1) + ret0, _ := ret[0].(string) + return ret0 +} + +// FindMultipathDeviceForDevice indicates an expected call of FindMultipathDeviceForDevice. +func (mr *MockDevicesMockRecorder) FindMultipathDeviceForDevice(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMultipathDeviceForDevice", reflect.TypeOf((*MockDevices)(nil).FindMultipathDeviceForDevice), arg0, arg1) +} + +// FlushDevice mocks base method. +func (m *MockDevices) FlushDevice(arg0 context.Context, arg1 *models.ScsiDeviceInfo, arg2 bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FlushDevice", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// FlushDevice indicates an expected call of FlushDevice. +func (mr *MockDevicesMockRecorder) FlushDevice(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushDevice", reflect.TypeOf((*MockDevices)(nil).FlushDevice), arg0, arg1, arg2) +} + +// FlushOneDevice mocks base method. +func (m *MockDevices) FlushOneDevice(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FlushOneDevice", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// FlushOneDevice indicates an expected call of FlushOneDevice. +func (mr *MockDevicesMockRecorder) FlushOneDevice(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushOneDevice", reflect.TypeOf((*MockDevices)(nil).FlushOneDevice), arg0, arg1) +} + +// GetDeviceFSType mocks base method. +func (m *MockDevices) GetDeviceFSType(arg0 context.Context, arg1 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDeviceFSType", arg0, arg1) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDeviceFSType indicates an expected call of GetDeviceFSType. +func (mr *MockDevicesMockRecorder) GetDeviceFSType(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceFSType", reflect.TypeOf((*MockDevices)(nil).GetDeviceFSType), arg0, arg1) +} + +// GetDiskSize mocks base method. +func (m *MockDevices) GetDiskSize(arg0 context.Context, arg1 string) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDiskSize", arg0, arg1) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDiskSize indicates an expected call of GetDiskSize. +func (mr *MockDevicesMockRecorder) GetDiskSize(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDiskSize", reflect.TypeOf((*MockDevices)(nil).GetDiskSize), arg0, arg1) +} + +// GetLUKSDeviceForMultipathDevice mocks base method. +func (m *MockDevices) GetLUKSDeviceForMultipathDevice(arg0 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLUKSDeviceForMultipathDevice", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLUKSDeviceForMultipathDevice indicates an expected call of GetLUKSDeviceForMultipathDevice. +func (mr *MockDevicesMockRecorder) GetLUKSDeviceForMultipathDevice(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDeviceForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).GetLUKSDeviceForMultipathDevice), arg0) +} + +// GetLunSerial mocks base method. +func (m *MockDevices) GetLunSerial(arg0 context.Context, arg1 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLunSerial", arg0, arg1) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLunSerial indicates an expected call of GetLunSerial. +func (mr *MockDevicesMockRecorder) GetLunSerial(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLunSerial", reflect.TypeOf((*MockDevices)(nil).GetLunSerial), arg0, arg1) +} + +// GetMultipathDeviceUUID mocks base method. +func (m *MockDevices) GetMultipathDeviceUUID(arg0 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMultipathDeviceUUID", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetMultipathDeviceUUID indicates an expected call of GetMultipathDeviceUUID. +func (mr *MockDevicesMockRecorder) GetMultipathDeviceUUID(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceUUID", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceUUID), arg0) +} + +// IsDeviceUnformatted mocks base method. +func (m *MockDevices) IsDeviceUnformatted(arg0 context.Context, arg1 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsDeviceUnformatted", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsDeviceUnformatted indicates an expected call of IsDeviceUnformatted. +func (mr *MockDevicesMockRecorder) IsDeviceUnformatted(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDeviceUnformatted", reflect.TypeOf((*MockDevices)(nil).IsDeviceUnformatted), arg0, arg1) +} + +// ListAllDevices mocks base method. +func (m *MockDevices) ListAllDevices(arg0 context.Context) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ListAllDevices", arg0) +} + +// ListAllDevices indicates an expected call of ListAllDevices. +func (mr *MockDevicesMockRecorder) ListAllDevices(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAllDevices", reflect.TypeOf((*MockDevices)(nil).ListAllDevices), arg0) +} + +// MultipathFlushDevice mocks base method. +func (m *MockDevices) MultipathFlushDevice(arg0 context.Context, arg1 *models.ScsiDeviceInfo) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MultipathFlushDevice", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// MultipathFlushDevice indicates an expected call of MultipathFlushDevice. +func (mr *MockDevicesMockRecorder) MultipathFlushDevice(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MultipathFlushDevice", reflect.TypeOf((*MockDevices)(nil).MultipathFlushDevice), arg0, arg1) +} + +// RemoveDevice mocks base method. +func (m *MockDevices) RemoveDevice(arg0 context.Context, arg1 []string, arg2 bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveDevice", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveDevice indicates an expected call of RemoveDevice. +func (mr *MockDevicesMockRecorder) RemoveDevice(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveDevice", reflect.TypeOf((*MockDevices)(nil).RemoveDevice), arg0, arg1, arg2) +} + +// RemoveMultipathDeviceMapping mocks base method. +func (m *MockDevices) RemoveMultipathDeviceMapping(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveMultipathDeviceMapping", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveMultipathDeviceMapping indicates an expected call of RemoveMultipathDeviceMapping. +func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMapping(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMapping", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMapping), arg0, arg1) +} + +// ScanTargetLUN mocks base method. +func (m *MockDevices) ScanTargetLUN(arg0 context.Context, arg1 int, arg2 []int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ScanTargetLUN", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// ScanTargetLUN indicates an expected call of ScanTargetLUN. +func (mr *MockDevicesMockRecorder) ScanTargetLUN(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanTargetLUN", reflect.TypeOf((*MockDevices)(nil).ScanTargetLUN), arg0, arg1, arg2) +} + +// VerifyMultipathDevice mocks base method. +func (m *MockDevices) VerifyMultipathDevice(arg0 context.Context, arg1 *models.VolumePublishInfo, arg2 []models.VolumePublishInfo, arg3 *models.ScsiDeviceInfo) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VerifyMultipathDevice", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// VerifyMultipathDevice indicates an expected call of VerifyMultipathDevice. +func (mr *MockDevicesMockRecorder) VerifyMultipathDevice(arg0, arg1, arg2, arg3 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDevice", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDevice), arg0, arg1, arg2, arg3) +} + +// VerifyMultipathDeviceSize mocks base method. +func (m *MockDevices) VerifyMultipathDeviceSize(arg0 context.Context, arg1, arg2 string) (int64, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VerifyMultipathDeviceSize", arg0, arg1, arg2) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// VerifyMultipathDeviceSize indicates an expected call of VerifyMultipathDeviceSize. +func (mr *MockDevicesMockRecorder) VerifyMultipathDeviceSize(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDeviceSize", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDeviceSize), arg0, arg1, arg2) +} + +// WaitForDevice mocks base method. +func (m *MockDevices) WaitForDevice(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitForDevice", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitForDevice indicates an expected call of WaitForDevice. +func (mr *MockDevicesMockRecorder) WaitForDevice(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevice", reflect.TypeOf((*MockDevices)(nil).WaitForDevice), arg0, arg1) +} + +// WaitForDevicesRemoval mocks base method. +func (m *MockDevices) WaitForDevicesRemoval(arg0 context.Context, arg1 string, arg2 []string, arg3 time.Duration) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitForDevicesRemoval", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitForDevicesRemoval indicates an expected call of WaitForDevicesRemoval. +func (mr *MockDevicesMockRecorder) WaitForDevicesRemoval(arg0, arg1, arg2, arg3 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevicesRemoval", reflect.TypeOf((*MockDevices)(nil).WaitForDevicesRemoval), arg0, arg1, arg2, arg3) +} diff --git a/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go b/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go new file mode 100644 index 000000000..fc741adec --- /dev/null +++ b/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go @@ -0,0 +1,141 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/netapp/trident/utils/devices/luks (interfaces: Device) +// +// Generated by this command: +// +// mockgen -destination=../../../mocks/mock_utils/mock_devices/mock_luks/mock_luks.go -package mock_luks github.com/netapp/trident/utils/devices/luks Device +// + +// Package mock_luks is a generated GoMock package. +package mock_luks + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockDevice is a mock of Device interface. +type MockDevice struct { + ctrl *gomock.Controller + recorder *MockDeviceMockRecorder +} + +// MockDeviceMockRecorder is the mock recorder for MockDevice. +type MockDeviceMockRecorder struct { + mock *MockDevice +} + +// NewMockDevice creates a new mock instance. +func NewMockDevice(ctrl *gomock.Controller) *MockDevice { + mock := &MockDevice{ctrl: ctrl} + mock.recorder = &MockDeviceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDevice) EXPECT() *MockDeviceMockRecorder { + return m.recorder +} + +// CheckPassphrase mocks base method. +func (m *MockDevice) CheckPassphrase(arg0 context.Context, arg1 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CheckPassphrase", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CheckPassphrase indicates an expected call of CheckPassphrase. +func (mr *MockDeviceMockRecorder) CheckPassphrase(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckPassphrase", reflect.TypeOf((*MockDevice)(nil).CheckPassphrase), arg0, arg1) +} + +// EnsureFormattedAndOpen mocks base method. +func (m *MockDevice) EnsureFormattedAndOpen(arg0 context.Context, arg1 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureFormattedAndOpen", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnsureFormattedAndOpen indicates an expected call of EnsureFormattedAndOpen. +func (mr *MockDeviceMockRecorder) EnsureFormattedAndOpen(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureFormattedAndOpen", reflect.TypeOf((*MockDevice)(nil).EnsureFormattedAndOpen), arg0, arg1) +} + +// EnsureLUKSDeviceMappedOnHost mocks base method. +func (m *MockDevice) EnsureLUKSDeviceMappedOnHost(arg0 context.Context, arg1 string, arg2 map[string]string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureLUKSDeviceMappedOnHost", arg0, arg1, arg2) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnsureLUKSDeviceMappedOnHost indicates an expected call of EnsureLUKSDeviceMappedOnHost. +func (mr *MockDeviceMockRecorder) EnsureLUKSDeviceMappedOnHost(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceMappedOnHost", reflect.TypeOf((*MockDevice)(nil).EnsureLUKSDeviceMappedOnHost), arg0, arg1, arg2) +} + +// MappedDeviceName mocks base method. +func (m *MockDevice) MappedDeviceName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MappedDeviceName") + ret0, _ := ret[0].(string) + return ret0 +} + +// MappedDeviceName indicates an expected call of MappedDeviceName. +func (mr *MockDeviceMockRecorder) MappedDeviceName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MappedDeviceName", reflect.TypeOf((*MockDevice)(nil).MappedDeviceName)) +} + +// MappedDevicePath mocks base method. +func (m *MockDevice) MappedDevicePath() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MappedDevicePath") + ret0, _ := ret[0].(string) + return ret0 +} + +// MappedDevicePath indicates an expected call of MappedDevicePath. +func (mr *MockDeviceMockRecorder) MappedDevicePath() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MappedDevicePath", reflect.TypeOf((*MockDevice)(nil).MappedDevicePath)) +} + +// RawDevicePath mocks base method. +func (m *MockDevice) RawDevicePath() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RawDevicePath") + ret0, _ := ret[0].(string) + return ret0 +} + +// RawDevicePath indicates an expected call of RawDevicePath. +func (mr *MockDeviceMockRecorder) RawDevicePath() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RawDevicePath", reflect.TypeOf((*MockDevice)(nil).RawDevicePath)) +} + +// RotatePassphrase mocks base method. +func (m *MockDevice) RotatePassphrase(arg0 context.Context, arg1, arg2, arg3 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RotatePassphrase", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// RotatePassphrase indicates an expected call of RotatePassphrase. +func (mr *MockDeviceMockRecorder) RotatePassphrase(arg0, arg1, arg2, arg3 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotatePassphrase", reflect.TypeOf((*MockDevice)(nil).RotatePassphrase), arg0, arg1, arg2, arg3) +} diff --git a/mocks/mock_utils/mock_exec/mock_error.go b/mocks/mock_utils/mock_exec/mock_error.go new file mode 100644 index 000000000..6093a903d --- /dev/null +++ b/mocks/mock_utils/mock_exec/mock_error.go @@ -0,0 +1,27 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. +package mock_exec + +import "fmt" + +// ExitErrorStub is a stub for exec.ExitError. +type MockExitError struct { + code int + Message string +} + +func NewMockExitError(code int, message string) *MockExitError { + return &MockExitError{ + code: code, + Message: message, + } +} + +// Error implements the error interface. +func (e *MockExitError) Error() string { + return fmt.Sprintf("exit code %v", e.code) +} + +// ExitCode implements the ExitError interface. +func (e *MockExitError) ExitCode() int { + return e.code +} diff --git a/mocks/mock_utils/mock_fcp/mock_fcp_client.go b/mocks/mock_utils/mock_fcp/mock_fcp_client.go index c772baf63..be07f5c2d 100644 --- a/mocks/mock_utils/mock_fcp/mock_fcp_client.go +++ b/mocks/mock_utils/mock_fcp/mock_fcp_client.go @@ -14,6 +14,7 @@ import ( reflect "reflect" time "time" + fcp "github.com/netapp/trident/utils/fcp" models "github.com/netapp/trident/utils/models" gomock "go.uber.org/mock/gomock" ) @@ -56,6 +57,21 @@ func (mr *MockFCPMockRecorder) AttachVolumeRetry(arg0, arg1, arg2, arg3, arg4, a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachVolumeRetry", reflect.TypeOf((*MockFCP)(nil).AttachVolumeRetry), arg0, arg1, arg2, arg3, arg4, arg5) } +// GetDeviceInfoForLUN mocks base method. +func (m *MockFCP) GetDeviceInfoForLUN(arg0 context.Context, arg1 int, arg2 string, arg3, arg4 bool) (*fcp.ScsiDeviceInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDeviceInfoForLUN", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(*fcp.ScsiDeviceInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDeviceInfoForLUN indicates an expected call of GetDeviceInfoForLUN. +func (mr *MockFCPMockRecorder) GetDeviceInfoForLUN(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceInfoForLUN", reflect.TypeOf((*MockFCP)(nil).GetDeviceInfoForLUN), arg0, arg1, arg2, arg3, arg4) +} + // IsAlreadyAttached mocks base method. func (m *MockFCP) IsAlreadyAttached(arg0 context.Context, arg1 int, arg2 string) bool { m.ctrl.T.Helper() diff --git a/mocks/mock_utils/mock_fcp/mock_reconcile_utils.go b/mocks/mock_utils/mock_fcp/mock_reconcile_utils.go index 62c783d32..f29dd46fc 100644 --- a/mocks/mock_utils/mock_fcp/mock_reconcile_utils.go +++ b/mocks/mock_utils/mock_fcp/mock_reconcile_utils.go @@ -69,51 +69,6 @@ func (mr *MockFcpReconcileUtilsMockRecorder) GetFCPHostSessionMapForTarget(arg0, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFCPHostSessionMapForTarget", reflect.TypeOf((*MockFcpReconcileUtils)(nil).GetFCPHostSessionMapForTarget), arg0, arg1) } -// GetMultipathDeviceBySerial mocks base method. -func (m *MockFcpReconcileUtils) GetMultipathDeviceBySerial(arg0 context.Context, arg1 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceBySerial", arg0, arg1) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetMultipathDeviceBySerial indicates an expected call of GetMultipathDeviceBySerial. -func (mr *MockFcpReconcileUtilsMockRecorder) GetMultipathDeviceBySerial(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceBySerial", reflect.TypeOf((*MockFcpReconcileUtils)(nil).GetMultipathDeviceBySerial), arg0, arg1) -} - -// GetMultipathDeviceDisks mocks base method. -func (m *MockFcpReconcileUtils) GetMultipathDeviceDisks(arg0 context.Context, arg1 string) ([]string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceDisks", arg0, arg1) - ret0, _ := ret[0].([]string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetMultipathDeviceDisks indicates an expected call of GetMultipathDeviceDisks. -func (mr *MockFcpReconcileUtilsMockRecorder) GetMultipathDeviceDisks(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceDisks", reflect.TypeOf((*MockFcpReconcileUtils)(nil).GetMultipathDeviceDisks), arg0, arg1) -} - -// GetMultipathDeviceUUID mocks base method. -func (m *MockFcpReconcileUtils) GetMultipathDeviceUUID(arg0 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceUUID", arg0) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetMultipathDeviceUUID indicates an expected call of GetMultipathDeviceUUID. -func (mr *MockFcpReconcileUtilsMockRecorder) GetMultipathDeviceUUID(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceUUID", reflect.TypeOf((*MockFcpReconcileUtils)(nil).GetMultipathDeviceUUID), arg0) -} - // GetSysfsBlockDirsForLUN mocks base method. func (m *MockFcpReconcileUtils) GetSysfsBlockDirsForLUN(arg0 int, arg1 []map[string]int) []string { m.ctrl.T.Helper() diff --git a/mocks/mock_utils/mock_filesystem/mock_filesystem_client.go b/mocks/mock_utils/mock_filesystem/mock_filesystem_client.go index fcb126e8b..21604e858 100644 --- a/mocks/mock_utils/mock_filesystem/mock_filesystem_client.go +++ b/mocks/mock_utils/mock_filesystem/mock_filesystem_client.go @@ -84,21 +84,6 @@ func (mr *MockFilesystemMockRecorder) FormatVolume(arg0, arg1, arg2, arg3 any) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FormatVolume", reflect.TypeOf((*MockFilesystem)(nil).FormatVolume), arg0, arg1, arg2, arg3) } -// GenerateAnonymousMemFile mocks base method. -func (m *MockFilesystem) GenerateAnonymousMemFile(arg0, arg1 string) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GenerateAnonymousMemFile", arg0, arg1) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GenerateAnonymousMemFile indicates an expected call of GenerateAnonymousMemFile. -func (mr *MockFilesystemMockRecorder) GenerateAnonymousMemFile(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateAnonymousMemFile", reflect.TypeOf((*MockFilesystem)(nil).GenerateAnonymousMemFile), arg0, arg1) -} - // GetDFOutput mocks base method. func (m *MockFilesystem) GetDFOutput(arg0 context.Context) ([]models.DFInfo, error) { m.ctrl.T.Helper() diff --git a/mocks/mock_utils/mock_iscsi/mock_iscsi_client.go b/mocks/mock_utils/mock_iscsi/mock_iscsi_client.go index 93b412dad..e4b9e4a34 100644 --- a/mocks/mock_utils/mock_iscsi/mock_iscsi_client.go +++ b/mocks/mock_utils/mock_iscsi/mock_iscsi_client.go @@ -68,6 +68,21 @@ func (mr *MockISCSIMockRecorder) AttachVolumeRetry(arg0, arg1, arg2, arg3, arg4, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachVolumeRetry", reflect.TypeOf((*MockISCSI)(nil).AttachVolumeRetry), arg0, arg1, arg2, arg3, arg4, arg5) } +// GetDeviceInfoForLUN mocks base method. +func (m *MockISCSI) GetDeviceInfoForLUN(arg0 context.Context, arg1 map[int]int, arg2 int, arg3 string, arg4 bool) (*models.ScsiDeviceInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDeviceInfoForLUN", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(*models.ScsiDeviceInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDeviceInfoForLUN indicates an expected call of GetDeviceInfoForLUN. +func (mr *MockISCSIMockRecorder) GetDeviceInfoForLUN(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceInfoForLUN", reflect.TypeOf((*MockISCSI)(nil).GetDeviceInfoForLUN), arg0, arg1, arg2, arg3, arg4) +} + // IsAlreadyAttached mocks base method. func (m *MockISCSI) IsAlreadyAttached(arg0 context.Context, arg1 int, arg2 string) bool { m.ctrl.T.Helper() @@ -110,6 +125,21 @@ func (mr *MockISCSIMockRecorder) PreChecks(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PreChecks", reflect.TypeOf((*MockISCSI)(nil).PreChecks), arg0) } +// PrepareDeviceForRemoval mocks base method. +func (m *MockISCSI) PrepareDeviceForRemoval(arg0 context.Context, arg1 *models.ScsiDeviceInfo, arg2 *models.VolumePublishInfo, arg3 []models.VolumePublishInfo, arg4, arg5 bool) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PrepareDeviceForRemoval", arg0, arg1, arg2, arg3, arg4, arg5) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PrepareDeviceForRemoval indicates an expected call of PrepareDeviceForRemoval. +func (mr *MockISCSIMockRecorder) PrepareDeviceForRemoval(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareDeviceForRemoval", reflect.TypeOf((*MockISCSI)(nil).PrepareDeviceForRemoval), arg0, arg1, arg2, arg3, arg4, arg5) +} + // RemoveLUNFromSessions mocks base method. func (m *MockISCSI) RemoveLUNFromSessions(arg0 context.Context, arg1 *models.VolumePublishInfo, arg2 *models.ISCSISessions) { m.ctrl.T.Helper() diff --git a/mocks/mock_utils/mock_iscsi/mock_iscsi_devices_client.go b/mocks/mock_utils/mock_iscsi/mock_iscsi_devices_client.go deleted file mode 100644 index e492df3ce..000000000 --- a/mocks/mock_utils/mock_iscsi/mock_iscsi_devices_client.go +++ /dev/null @@ -1,378 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/netapp/trident/utils/iscsi (interfaces: Devices) -// -// Generated by this command: -// -// mockgen -destination=../../mocks/mock_utils/mock_iscsi/mock_iscsi_devices_client.go github.com/netapp/trident/utils/iscsi Devices -// - -// Package mock_iscsi is a generated GoMock package. -package mock_iscsi - -import ( - context "context" - reflect "reflect" - - models "github.com/netapp/trident/utils/models" - gomock "go.uber.org/mock/gomock" -) - -// MockDevices is a mock of Devices interface. -type MockDevices struct { - ctrl *gomock.Controller - recorder *MockDevicesMockRecorder -} - -// MockDevicesMockRecorder is the mock recorder for MockDevices. -type MockDevicesMockRecorder struct { - mock *MockDevices -} - -// NewMockDevices creates a new mock instance. -func NewMockDevices(ctrl *gomock.Controller) *MockDevices { - mock := &MockDevices{ctrl: ctrl} - mock.recorder = &MockDevicesMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDevices) EXPECT() *MockDevicesMockRecorder { - return m.recorder -} - -// CloseLUKSDevice mocks base method. -func (m *MockDevices) CloseLUKSDevice(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CloseLUKSDevice", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// CloseLUKSDevice indicates an expected call of CloseLUKSDevice. -func (mr *MockDevicesMockRecorder) CloseLUKSDevice(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseLUKSDevice", reflect.TypeOf((*MockDevices)(nil).CloseLUKSDevice), arg0, arg1) -} - -// CompareWithAllPublishInfos mocks base method. -func (m *MockDevices) CompareWithAllPublishInfos(arg0 context.Context, arg1 *models.VolumePublishInfo, arg2 []models.VolumePublishInfo, arg3 *models.ScsiDeviceInfo) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CompareWithAllPublishInfos", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// CompareWithAllPublishInfos indicates an expected call of CompareWithAllPublishInfos. -func (mr *MockDevicesMockRecorder) CompareWithAllPublishInfos(arg0, arg1, arg2, arg3 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareWithAllPublishInfos", reflect.TypeOf((*MockDevices)(nil).CompareWithAllPublishInfos), arg0, arg1, arg2, arg3) -} - -// CompareWithPublishedDevicePath mocks base method. -func (m *MockDevices) CompareWithPublishedDevicePath(arg0 context.Context, arg1 *models.VolumePublishInfo, arg2 *models.ScsiDeviceInfo) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CompareWithPublishedDevicePath", arg0, arg1, arg2) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CompareWithPublishedDevicePath indicates an expected call of CompareWithPublishedDevicePath. -func (mr *MockDevicesMockRecorder) CompareWithPublishedDevicePath(arg0, arg1, arg2 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareWithPublishedDevicePath", reflect.TypeOf((*MockDevices)(nil).CompareWithPublishedDevicePath), arg0, arg1, arg2) -} - -// CompareWithPublishedSerialNumber mocks base method. -func (m *MockDevices) CompareWithPublishedSerialNumber(arg0 context.Context, arg1 *models.VolumePublishInfo, arg2 *models.ScsiDeviceInfo) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CompareWithPublishedSerialNumber", arg0, arg1, arg2) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CompareWithPublishedSerialNumber indicates an expected call of CompareWithPublishedSerialNumber. -func (mr *MockDevicesMockRecorder) CompareWithPublishedSerialNumber(arg0, arg1, arg2 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareWithPublishedSerialNumber", reflect.TypeOf((*MockDevices)(nil).CompareWithPublishedSerialNumber), arg0, arg1, arg2) -} - -// EnsureDeviceReadable mocks base method. -func (m *MockDevices) EnsureDeviceReadable(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureDeviceReadable", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// EnsureDeviceReadable indicates an expected call of EnsureDeviceReadable. -func (mr *MockDevicesMockRecorder) EnsureDeviceReadable(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceReadable", reflect.TypeOf((*MockDevices)(nil).EnsureDeviceReadable), arg0, arg1) -} - -// EnsureLUKSDeviceClosed mocks base method. -func (m *MockDevices) EnsureLUKSDeviceClosed(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosed", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// EnsureLUKSDeviceClosed indicates an expected call of EnsureLUKSDeviceClosed. -func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosed(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosed", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosed), arg0, arg1) -} - -// EnsureLUKSDeviceClosedWithMaxWaitLimit mocks base method. -func (m *MockDevices) EnsureLUKSDeviceClosedWithMaxWaitLimit(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosedWithMaxWaitLimit", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// EnsureLUKSDeviceClosedWithMaxWaitLimit indicates an expected call of EnsureLUKSDeviceClosedWithMaxWaitLimit. -func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosedWithMaxWaitLimit(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosedWithMaxWaitLimit", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosedWithMaxWaitLimit), arg0, arg1) -} - -// EnsureLUKSDeviceMappedOnHost mocks base method. -func (m *MockDevices) EnsureLUKSDeviceMappedOnHost(arg0 context.Context, arg1 models.LUKSDeviceInterface, arg2 string, arg3 map[string]string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureLUKSDeviceMappedOnHost", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EnsureLUKSDeviceMappedOnHost indicates an expected call of EnsureLUKSDeviceMappedOnHost. -func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceMappedOnHost(arg0, arg1, arg2, arg3 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceMappedOnHost", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceMappedOnHost), arg0, arg1, arg2, arg3) -} - -// GetDMDeviceForMapperPath mocks base method. -func (m *MockDevices) GetDMDeviceForMapperPath(arg0 context.Context, arg1 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDMDeviceForMapperPath", arg0, arg1) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetDMDeviceForMapperPath indicates an expected call of GetDMDeviceForMapperPath. -func (mr *MockDevicesMockRecorder) GetDMDeviceForMapperPath(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDMDeviceForMapperPath", reflect.TypeOf((*MockDevices)(nil).GetDMDeviceForMapperPath), arg0, arg1) -} - -// GetDeviceFSType mocks base method. -func (m *MockDevices) GetDeviceFSType(arg0 context.Context, arg1 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeviceFSType", arg0, arg1) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetDeviceFSType indicates an expected call of GetDeviceFSType. -func (mr *MockDevicesMockRecorder) GetDeviceFSType(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceFSType", reflect.TypeOf((*MockDevices)(nil).GetDeviceFSType), arg0, arg1) -} - -// GetDeviceInfoForLUN mocks base method. -func (m *MockDevices) GetDeviceInfoForLUN(arg0 context.Context, arg1 map[int]int, arg2 int, arg3 string, arg4 bool) (*models.ScsiDeviceInfo, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeviceInfoForLUN", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(*models.ScsiDeviceInfo) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetDeviceInfoForLUN indicates an expected call of GetDeviceInfoForLUN. -func (mr *MockDevicesMockRecorder) GetDeviceInfoForLUN(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceInfoForLUN", reflect.TypeOf((*MockDevices)(nil).GetDeviceInfoForLUN), arg0, arg1, arg2, arg3, arg4) -} - -// GetISCSIDiskSize mocks base method. -func (m *MockDevices) GetISCSIDiskSize(arg0 context.Context, arg1 string) (int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetISCSIDiskSize", arg0, arg1) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetISCSIDiskSize indicates an expected call of GetISCSIDiskSize. -func (mr *MockDevicesMockRecorder) GetISCSIDiskSize(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetISCSIDiskSize", reflect.TypeOf((*MockDevices)(nil).GetISCSIDiskSize), arg0, arg1) -} - -// GetLUKSDeviceForMultipathDevice mocks base method. -func (m *MockDevices) GetLUKSDeviceForMultipathDevice(arg0 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLUKSDeviceForMultipathDevice", arg0) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetLUKSDeviceForMultipathDevice indicates an expected call of GetLUKSDeviceForMultipathDevice. -func (mr *MockDevicesMockRecorder) GetLUKSDeviceForMultipathDevice(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDeviceForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).GetLUKSDeviceForMultipathDevice), arg0) -} - -// GetMountedISCSIDevices mocks base method. -func (m *MockDevices) GetMountedISCSIDevices(arg0 context.Context) ([]*models.ScsiDeviceInfo, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMountedISCSIDevices", arg0) - ret0, _ := ret[0].([]*models.ScsiDeviceInfo) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetMountedISCSIDevices indicates an expected call of GetMountedISCSIDevices. -func (mr *MockDevicesMockRecorder) GetMountedISCSIDevices(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMountedISCSIDevices", reflect.TypeOf((*MockDevices)(nil).GetMountedISCSIDevices), arg0) -} - -// GetUnderlyingDevicePathForLUKSDevice mocks base method. -func (m *MockDevices) GetUnderlyingDevicePathForLUKSDevice(arg0 context.Context, arg1 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUnderlyingDevicePathForLUKSDevice", arg0, arg1) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetUnderlyingDevicePathForLUKSDevice indicates an expected call of GetUnderlyingDevicePathForLUKSDevice. -func (mr *MockDevicesMockRecorder) GetUnderlyingDevicePathForLUKSDevice(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnderlyingDevicePathForLUKSDevice", reflect.TypeOf((*MockDevices)(nil).GetUnderlyingDevicePathForLUKSDevice), arg0, arg1) -} - -// IsDeviceUnformatted mocks base method. -func (m *MockDevices) IsDeviceUnformatted(arg0 context.Context, arg1 string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsDeviceUnformatted", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsDeviceUnformatted indicates an expected call of IsDeviceUnformatted. -func (mr *MockDevicesMockRecorder) IsDeviceUnformatted(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDeviceUnformatted", reflect.TypeOf((*MockDevices)(nil).IsDeviceUnformatted), arg0, arg1) -} - -// MultipathFlushDevice mocks base method. -func (m *MockDevices) MultipathFlushDevice(arg0 context.Context, arg1 *models.ScsiDeviceInfo) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MultipathFlushDevice", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// MultipathFlushDevice indicates an expected call of MultipathFlushDevice. -func (mr *MockDevicesMockRecorder) MultipathFlushDevice(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MultipathFlushDevice", reflect.TypeOf((*MockDevices)(nil).MultipathFlushDevice), arg0, arg1) -} - -// NewLUKSDevice mocks base method. -func (m *MockDevices) NewLUKSDevice(arg0, arg1 string) (models.LUKSDeviceInterface, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewLUKSDevice", arg0, arg1) - ret0, _ := ret[0].(models.LUKSDeviceInterface) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NewLUKSDevice indicates an expected call of NewLUKSDevice. -func (mr *MockDevicesMockRecorder) NewLUKSDevice(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewLUKSDevice", reflect.TypeOf((*MockDevices)(nil).NewLUKSDevice), arg0, arg1) -} - -// NewLUKSDeviceFromMappingPath mocks base method. -func (m *MockDevices) NewLUKSDeviceFromMappingPath(arg0 context.Context, arg1, arg2 string) (models.LUKSDeviceInterface, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewLUKSDeviceFromMappingPath", arg0, arg1, arg2) - ret0, _ := ret[0].(models.LUKSDeviceInterface) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NewLUKSDeviceFromMappingPath indicates an expected call of NewLUKSDeviceFromMappingPath. -func (mr *MockDevicesMockRecorder) NewLUKSDeviceFromMappingPath(arg0, arg1, arg2 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewLUKSDeviceFromMappingPath", reflect.TypeOf((*MockDevices)(nil).NewLUKSDeviceFromMappingPath), arg0, arg1, arg2) -} - -// PrepareDeviceForRemoval mocks base method. -func (m *MockDevices) PrepareDeviceForRemoval(arg0 context.Context, arg1 *models.ScsiDeviceInfo, arg2 *models.VolumePublishInfo, arg3 []models.VolumePublishInfo, arg4, arg5 bool) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PrepareDeviceForRemoval", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PrepareDeviceForRemoval indicates an expected call of PrepareDeviceForRemoval. -func (mr *MockDevicesMockRecorder) PrepareDeviceForRemoval(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareDeviceForRemoval", reflect.TypeOf((*MockDevices)(nil).PrepareDeviceForRemoval), arg0, arg1, arg2, arg3, arg4, arg5) -} - -// RemoveMultipathDeviceMapping mocks base method. -func (m *MockDevices) RemoveMultipathDeviceMapping(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveMultipathDeviceMapping", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// RemoveMultipathDeviceMapping indicates an expected call of RemoveMultipathDeviceMapping. -func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMapping(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMapping", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMapping), arg0, arg1) -} - -// RemoveSCSIDevice mocks base method. -func (m *MockDevices) RemoveSCSIDevice(arg0 context.Context, arg1 *models.ScsiDeviceInfo, arg2, arg3 bool) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveSCSIDevice", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RemoveSCSIDevice indicates an expected call of RemoveSCSIDevice. -func (mr *MockDevicesMockRecorder) RemoveSCSIDevice(arg0, arg1, arg2, arg3 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveSCSIDevice", reflect.TypeOf((*MockDevices)(nil).RemoveSCSIDevice), arg0, arg1, arg2, arg3) -} - -// WaitForDevice mocks base method. -func (m *MockDevices) WaitForDevice(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitForDevice", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// WaitForDevice indicates an expected call of WaitForDevice. -func (mr *MockDevicesMockRecorder) WaitForDevice(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevice", reflect.TypeOf((*MockDevices)(nil).WaitForDevice), arg0, arg1) -} diff --git a/mocks/mock_utils/mock_iscsi/mock_reconcile_utils.go b/mocks/mock_utils/mock_iscsi/mock_reconcile_utils.go index 9975d068a..d2cd5f145 100644 --- a/mocks/mock_utils/mock_iscsi/mock_reconcile_utils.go +++ b/mocks/mock_utils/mock_iscsi/mock_reconcile_utils.go @@ -69,51 +69,6 @@ func (mr *MockIscsiReconcileUtilsMockRecorder) GetISCSIHostSessionMapForTarget(a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetISCSIHostSessionMapForTarget", reflect.TypeOf((*MockIscsiReconcileUtils)(nil).GetISCSIHostSessionMapForTarget), arg0, arg1) } -// GetMultipathDeviceBySerial mocks base method. -func (m *MockIscsiReconcileUtils) GetMultipathDeviceBySerial(arg0 context.Context, arg1 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceBySerial", arg0, arg1) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetMultipathDeviceBySerial indicates an expected call of GetMultipathDeviceBySerial. -func (mr *MockIscsiReconcileUtilsMockRecorder) GetMultipathDeviceBySerial(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceBySerial", reflect.TypeOf((*MockIscsiReconcileUtils)(nil).GetMultipathDeviceBySerial), arg0, arg1) -} - -// GetMultipathDeviceDisks mocks base method. -func (m *MockIscsiReconcileUtils) GetMultipathDeviceDisks(arg0 context.Context, arg1 string) ([]string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceDisks", arg0, arg1) - ret0, _ := ret[0].([]string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetMultipathDeviceDisks indicates an expected call of GetMultipathDeviceDisks. -func (mr *MockIscsiReconcileUtilsMockRecorder) GetMultipathDeviceDisks(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceDisks", reflect.TypeOf((*MockIscsiReconcileUtils)(nil).GetMultipathDeviceDisks), arg0, arg1) -} - -// GetMultipathDeviceUUID mocks base method. -func (m *MockIscsiReconcileUtils) GetMultipathDeviceUUID(arg0 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceUUID", arg0) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetMultipathDeviceUUID indicates an expected call of GetMultipathDeviceUUID. -func (mr *MockIscsiReconcileUtilsMockRecorder) GetMultipathDeviceUUID(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceUUID", reflect.TypeOf((*MockIscsiReconcileUtils)(nil).GetMultipathDeviceUUID), arg0) -} - // GetSysfsBlockDirsForLUN mocks base method. func (m *MockIscsiReconcileUtils) GetSysfsBlockDirsForLUN(arg0 int, arg1 map[int]int) []string { m.ctrl.T.Helper() diff --git a/mocks/mock_utils/mock_models/mock_luks/mock_luks.go b/mocks/mock_utils/mock_models/mock_luks/mock_luks.go deleted file mode 100644 index 9fdf74af6..000000000 --- a/mocks/mock_utils/mock_models/mock_luks/mock_luks.go +++ /dev/null @@ -1,198 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/netapp/trident/utils/models (interfaces: LUKSDeviceInterface) -// -// Generated by this command: -// -// mockgen -destination=../../mocks/mock_utils/mock_models/mock_luks/mock_luks.go -package mock_luks github.com/netapp/trident/utils/models LUKSDeviceInterface -// - -// Package mock_luks is a generated GoMock package. -package mock_luks - -import ( - context "context" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockLUKSDeviceInterface is a mock of LUKSDeviceInterface interface. -type MockLUKSDeviceInterface struct { - ctrl *gomock.Controller - recorder *MockLUKSDeviceInterfaceMockRecorder -} - -// MockLUKSDeviceInterfaceMockRecorder is the mock recorder for MockLUKSDeviceInterface. -type MockLUKSDeviceInterfaceMockRecorder struct { - mock *MockLUKSDeviceInterface -} - -// NewMockLUKSDeviceInterface creates a new mock instance. -func NewMockLUKSDeviceInterface(ctrl *gomock.Controller) *MockLUKSDeviceInterface { - mock := &MockLUKSDeviceInterface{ctrl: ctrl} - mock.recorder = &MockLUKSDeviceInterfaceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockLUKSDeviceInterface) EXPECT() *MockLUKSDeviceInterfaceMockRecorder { - return m.recorder -} - -// CheckPassphrase mocks base method. -func (m *MockLUKSDeviceInterface) CheckPassphrase(arg0 context.Context, arg1 string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckPassphrase", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CheckPassphrase indicates an expected call of CheckPassphrase. -func (mr *MockLUKSDeviceInterfaceMockRecorder) CheckPassphrase(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckPassphrase", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).CheckPassphrase), arg0, arg1) -} - -// EnsureFormattedAndOpen mocks base method. -func (m *MockLUKSDeviceInterface) EnsureFormattedAndOpen(arg0 context.Context, arg1 string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureFormattedAndOpen", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EnsureFormattedAndOpen indicates an expected call of EnsureFormattedAndOpen. -func (mr *MockLUKSDeviceInterfaceMockRecorder) EnsureFormattedAndOpen(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureFormattedAndOpen", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).EnsureFormattedAndOpen), arg0, arg1) -} - -// IsLUKSFormatted mocks base method. -func (m *MockLUKSDeviceInterface) IsLUKSFormatted(arg0 context.Context) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsLUKSFormatted", arg0) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsLUKSFormatted indicates an expected call of IsLUKSFormatted. -func (mr *MockLUKSDeviceInterfaceMockRecorder) IsLUKSFormatted(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsLUKSFormatted", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).IsLUKSFormatted), arg0) -} - -// IsOpen mocks base method. -func (m *MockLUKSDeviceInterface) IsOpen(arg0 context.Context) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsOpen", arg0) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsOpen indicates an expected call of IsOpen. -func (mr *MockLUKSDeviceInterfaceMockRecorder) IsOpen(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOpen", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).IsOpen), arg0) -} - -// LUKSFormat mocks base method. -func (m *MockLUKSDeviceInterface) LUKSFormat(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LUKSFormat", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// LUKSFormat indicates an expected call of LUKSFormat. -func (mr *MockLUKSDeviceInterfaceMockRecorder) LUKSFormat(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LUKSFormat", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).LUKSFormat), arg0, arg1) -} - -// MappedDeviceName mocks base method. -func (m *MockLUKSDeviceInterface) MappedDeviceName() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MappedDeviceName") - ret0, _ := ret[0].(string) - return ret0 -} - -// MappedDeviceName indicates an expected call of MappedDeviceName. -func (mr *MockLUKSDeviceInterfaceMockRecorder) MappedDeviceName() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MappedDeviceName", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).MappedDeviceName)) -} - -// MappedDevicePath mocks base method. -func (m *MockLUKSDeviceInterface) MappedDevicePath() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MappedDevicePath") - ret0, _ := ret[0].(string) - return ret0 -} - -// MappedDevicePath indicates an expected call of MappedDevicePath. -func (mr *MockLUKSDeviceInterfaceMockRecorder) MappedDevicePath() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MappedDevicePath", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).MappedDevicePath)) -} - -// Open mocks base method. -func (m *MockLUKSDeviceInterface) Open(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Open", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Open indicates an expected call of Open. -func (mr *MockLUKSDeviceInterfaceMockRecorder) Open(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).Open), arg0, arg1) -} - -// RawDevicePath mocks base method. -func (m *MockLUKSDeviceInterface) RawDevicePath() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RawDevicePath") - ret0, _ := ret[0].(string) - return ret0 -} - -// RawDevicePath indicates an expected call of RawDevicePath. -func (mr *MockLUKSDeviceInterfaceMockRecorder) RawDevicePath() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RawDevicePath", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).RawDevicePath)) -} - -// Resize mocks base method. -func (m *MockLUKSDeviceInterface) Resize(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Resize", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Resize indicates an expected call of Resize. -func (mr *MockLUKSDeviceInterfaceMockRecorder) Resize(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resize", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).Resize), arg0, arg1) -} - -// RotatePassphrase mocks base method. -func (m *MockLUKSDeviceInterface) RotatePassphrase(arg0 context.Context, arg1, arg2, arg3 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RotatePassphrase", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// RotatePassphrase indicates an expected call of RotatePassphrase. -func (mr *MockLUKSDeviceInterfaceMockRecorder) RotatePassphrase(arg0, arg1, arg2, arg3 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotatePassphrase", reflect.TypeOf((*MockLUKSDeviceInterface)(nil).RotatePassphrase), arg0, arg1, arg2, arg3) -} diff --git a/storage_drivers/ontap/ontap_common.go b/storage_drivers/ontap/ontap_common.go index 9af9ca84b..8e675b9bc 100644 --- a/storage_drivers/ontap/ontap_common.go +++ b/storage_drivers/ontap/ontap_common.go @@ -33,6 +33,7 @@ import ( "github.com/netapp/trident/storage_drivers/ontap/api/azgo" "github.com/netapp/trident/storage_drivers/ontap/api/rest/models" "github.com/netapp/trident/utils" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/fcp" "github.com/netapp/trident/utils/filesystem" @@ -3772,7 +3773,7 @@ func incrementWithLUKSMetadataIfLUKSEnabled(ctx context.Context, size uint64, lu } if isLUKS { - return size + utils.LUKSMetadataSize + return size + luks.LUKSMetadataSize } return size @@ -3785,13 +3786,13 @@ func decrementWithLUKSMetadataIfLUKSEnabled(ctx context.Context, size uint64, lu Logc(ctx).WithError(err).Debug("Could not parse luksEncryption string.") } - if utils.LUKSMetadataSize > size { + if luks.LUKSMetadataSize > size { Logc(ctx).WithError(err).WithField("size", size).Error("Size too small to subtract LUKS metadata.") return 0 } if isLUKS { - return size - utils.LUKSMetadataSize + return size - luks.LUKSMetadataSize } return size diff --git a/storage_drivers/ontap/ontap_san.go b/storage_drivers/ontap/ontap_san.go index a852eb526..be0202d91 100644 --- a/storage_drivers/ontap/ontap_san.go +++ b/storage_drivers/ontap/ontap_san.go @@ -21,6 +21,7 @@ import ( "github.com/netapp/trident/storage_drivers/ontap/api" "github.com/netapp/trident/storage_drivers/ontap/awsapi" "github.com/netapp/trident/utils" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/iscsi" "github.com/netapp/trident/utils/models" @@ -97,7 +98,7 @@ func (d *SANStorageDriver) Initialize( // Initialize the iSCSI client var err error - d.iscsi, err = iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + d.iscsi, err = iscsi.New(utils.NewOSClient()) if err != nil { return fmt.Errorf("could not initialize iSCSI client; %v", err) } @@ -731,7 +732,7 @@ func (d *SANStorageDriver) Import(ctx context.Context, volConfig *storage.Volume volConfig.Size = lunInfo.Size if utils.ParseBool(volConfig.LUKSEncryption) { - newSize, err := subtractUintFromSizeString(volConfig.Size, utils.LUKSMetadataSize) + newSize, err := subtractUintFromSizeString(volConfig.Size, luks.LUKSMetadataSize) if err != nil { return err } diff --git a/storage_drivers/ontap/ontap_san_economy.go b/storage_drivers/ontap/ontap_san_economy.go index 0e810b350..0661220c5 100644 --- a/storage_drivers/ontap/ontap_san_economy.go +++ b/storage_drivers/ontap/ontap_san_economy.go @@ -21,6 +21,7 @@ import ( "github.com/netapp/trident/storage_drivers/ontap/awsapi" "github.com/netapp/trident/utils" "github.com/netapp/trident/utils/crypto" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/iscsi" "github.com/netapp/trident/utils/models" @@ -252,7 +253,7 @@ func (d *SANEconomyStorageDriver) Initialize( // Initialize the iSCSI client var err error - d.iscsi, err = iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + d.iscsi, err = iscsi.New(utils.NewOSClient()) if err != nil { return fmt.Errorf("error initializing iSCSI client: %v", err) } @@ -909,7 +910,7 @@ func (d *SANEconomyStorageDriver) Import( volConfig.Size = extantLUN.Size if utils.ParseBool(volConfig.LUKSEncryption) { - newSize, err := subtractUintFromSizeString(volConfig.Size, utils.LUKSMetadataSize) + newSize, err := subtractUintFromSizeString(volConfig.Size, luks.LUKSMetadataSize) if err != nil { return err } diff --git a/storage_drivers/ontap/ontap_san_economy_test.go b/storage_drivers/ontap/ontap_san_economy_test.go index 099383a36..90a0945db 100644 --- a/storage_drivers/ontap/ontap_san_economy_test.go +++ b/storage_drivers/ontap/ontap_san_economy_test.go @@ -196,7 +196,7 @@ func newTestOntapSanEcoDriver(t *testing.T, vserverAdminHost, vserverAdminPort, config.AWSConfig.FSxFilesystemID = *fsxId } - iscsiClient, err := iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + iscsiClient, err := iscsi.New(utils.NewOSClient()) assert.NoError(t, err) sanEcoDriver := &SANEconomyStorageDriver{ diff --git a/storage_drivers/ontap/ontap_san_nvme.go b/storage_drivers/ontap/ontap_san_nvme.go index 5734fa257..d08acd703 100644 --- a/storage_drivers/ontap/ontap_san_nvme.go +++ b/storage_drivers/ontap/ontap_san_nvme.go @@ -24,6 +24,7 @@ import ( "github.com/netapp/trident/storage_drivers/ontap/api" "github.com/netapp/trident/storage_drivers/ontap/awsapi" "github.com/netapp/trident/utils" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/filesystem" "github.com/netapp/trident/utils/models" @@ -664,7 +665,7 @@ func (d *NVMeStorageDriver) Import(ctx context.Context, volConfig *storage.Volum // If the import is a LUKS encrypted volume, then remove the LUKS metadata overhead from the reported // size on the volConfig. if utils.ParseBool(volConfig.LUKSEncryption) { - newSize, err := subtractUintFromSizeString(volConfig.Size, utils.LUKSMetadataSize) + newSize, err := subtractUintFromSizeString(volConfig.Size, luks.LUKSMetadataSize) if err != nil { return err } diff --git a/storage_drivers/ontap/ontap_san_test.go b/storage_drivers/ontap/ontap_san_test.go index 21b66f1cc..ce118d554 100644 --- a/storage_drivers/ontap/ontap_san_test.go +++ b/storage_drivers/ontap/ontap_san_test.go @@ -71,7 +71,7 @@ func newMockOntapSANDriver(t *testing.T) (*mockapi.MockOntapAPI, *SANStorageDriv driver.API = mockAPI driver.ips = []string{"127.0.0.1"} - iscsiClient, err := iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + iscsiClient, err := iscsi.New(utils.NewOSClient()) assert.NoError(t, err) driver.iscsi = iscsiClient diff --git a/storage_drivers/solidfire/solidfire_san.go b/storage_drivers/solidfire/solidfire_san.go index eb6566b02..e5abd87ed 100644 --- a/storage_drivers/solidfire/solidfire_san.go +++ b/storage_drivers/solidfire/solidfire_san.go @@ -145,7 +145,7 @@ func (d *SANStorageDriver) Initialize( // Initialize the iSCSI client var err error - d.iscsi, err = iscsi.New(utils.NewOSClient(), utils.NewDevicesClient()) + d.iscsi, err = iscsi.New(utils.NewOSClient()) if err != nil { return fmt.Errorf("could not initialize iSCSI client: %v", err) } diff --git a/utils/adaptors.go b/utils/adaptors.go index 192999771..f4c365139 100644 --- a/utils/adaptors.go +++ b/utils/adaptors.go @@ -20,121 +20,35 @@ func (c OSClient) PathExists(path string) (bool, error) { return PathExists(path) } -type DevicesClient struct{} - -func NewDevicesClient() DevicesClient { - return DevicesClient{} -} - -func (c DevicesClient) WaitForDevice(ctx context.Context, device string) error { - return waitForDevice(ctx, device) -} - -func (c DevicesClient) GetDeviceFSType(ctx context.Context, device string) (string, error) { - return getDeviceFSType(ctx, device) -} - -func (c DevicesClient) NewLUKSDevice(rawDevicePath, volumeId string) (models.LUKSDeviceInterface, error) { - return NewLUKSDevice(rawDevicePath, volumeId) -} - -func (c DevicesClient) MultipathFlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo) error { - return multipathFlushDevice(ctx, deviceInfo) -} - -func (c DevicesClient) CompareWithPublishedDevicePath( - ctx context.Context, publishInfo *models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, -) (bool, error) { - return compareWithPublishedDevicePath(ctx, publishInfo, deviceInfo) -} - -func (c DevicesClient) CompareWithPublishedSerialNumber( - ctx context.Context, publishInfo *models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, -) (bool, error) { - return compareWithPublishedSerialNumber(ctx, publishInfo, deviceInfo) -} - -func (c DevicesClient) CompareWithAllPublishInfos( - ctx context.Context, publishInfo *models.VolumePublishInfo, - allPublishInfos []models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, -) error { - return compareWithAllPublishInfos(ctx, publishInfo, allPublishInfos, deviceInfo) -} - -func (c DevicesClient) RemoveSCSIDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, ignoreErrors, - skipFlush bool, -) (bool, error) { - return removeSCSIDevice(ctx, deviceInfo, ignoreErrors, skipFlush) -} - -func (c DevicesClient) GetDeviceInfoForLUN( +func GetDeviceInfoForLUN( ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, isDetachCall bool, ) (*models.ScsiDeviceInfo, error) { - return GetDeviceInfoForLUN(ctx, hostSessionMap, lunID, iSCSINodeName, isDetachCall) -} - -func (c DevicesClient) EnsureLUKSDeviceMappedOnHost(ctx context.Context, luksDevice models.LUKSDeviceInterface, - name string, secrets map[string]string, -) (bool, error) { - return EnsureLUKSDeviceMappedOnHost(ctx, luksDevice, name, secrets) -} - -func (c DevicesClient) CloseLUKSDevice(ctx context.Context, devicePath string) error { - return closeLUKSDevice(ctx, devicePath) -} - -func (c DevicesClient) IsDeviceUnformatted(ctx context.Context, device string) (bool, error) { - return isDeviceUnformatted(ctx, device) + return iscsiClient.GetDeviceInfoForLUN(ctx, hostSessionMap, lunID, iSCSINodeName, isDetachCall) } -func (c DevicesClient) EnsureDeviceReadable(ctx context.Context, device string) error { - return ensureDeviceReadable(ctx, device) -} - -func (c DevicesClient) GetISCSIDiskSize(ctx context.Context, devicePath string) (int64, error) { - return getISCSIDiskSize(ctx, devicePath) -} - -func (c DevicesClient) GetFCPDiskSize(ctx context.Context, devicePath string) (int64, error) { - return getFCPDiskSize(ctx, devicePath) -} - -func (c DevicesClient) GetMountedISCSIDevices(ctx context.Context) ([]*models.ScsiDeviceInfo, error) { - return GetMountedISCSIDevices(ctx) -} - -func (c DevicesClient) GetUnderlyingDevicePathForLUKSDevice(ctx context.Context, luksDevicePath string) (string, error) { - return GetUnderlyingDevicePathForLUKSDevice(ctx, luksDevicePath) -} - -func (c DevicesClient) RemoveMultipathDeviceMapping(ctx context.Context, devicePath string) error { - return RemoveMultipathDeviceMapping(ctx, devicePath) -} - -func (c DevicesClient) GetDMDeviceForMapperPath(ctx context.Context, mapperPath string) (string, error) { - return GetDMDeviceForMapperPath(ctx, mapperPath) -} - -func (c DevicesClient) EnsureLUKSDeviceClosed(ctx context.Context, devicePath string) error { - return EnsureLUKSDeviceClosed(ctx, devicePath) -} - -func (c DevicesClient) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { - return EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksDevicePath) -} - -func (c DevicesClient) PrepareDeviceForRemoval(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, publishInfo *models.VolumePublishInfo, +func GetDeviceInfoForFCPLUN( + ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, isDetachCall bool, +) (*models.ScsiDeviceInfo, error) { + // TODO: check if we require both isDetachCall and needFSType + deviceInfo, err := fcpClient.GetDeviceInfoForLUN(ctx, lunID, iSCSINodeName, false, isDetachCall) + if err != nil { + return nil, err + } + + return &models.ScsiDeviceInfo{ + Host: deviceInfo.Host, + Channel: deviceInfo.Channel, + Target: deviceInfo.Target, + LUN: deviceInfo.LUN, + Devices: deviceInfo.Devices, + MultipathDevice: deviceInfo.MultipathDevice, + WWNN: deviceInfo.WWNN, + SessionNumber: deviceInfo.SessionNumber, + }, nil +} + +func PrepareDeviceForRemoval(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, publishInfo *models.VolumePublishInfo, allPublishInfos []models.VolumePublishInfo, ignoreErrors, force bool, ) (string, error) { - return PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, allPublishInfos, ignoreErrors, force) -} - -func (c DevicesClient) GetLUKSDeviceForMultipathDevice(multipathDevice string) (string, error) { - return GetLUKSDeviceForMultipathDevice(multipathDevice) -} - -func (c DevicesClient) NewLUKSDeviceFromMappingPath(ctx context.Context, mappingPath, - volumeId string, -) (models.LUKSDeviceInterface, error) { - return NewLUKSDeviceFromMappingPath(ctx, mappingPath, volumeId) + return iscsiClient.PrepareDeviceForRemoval(ctx, deviceInfo, publishInfo, allPublishInfos, ignoreErrors, force) } diff --git a/utils/devices.go b/utils/devices.go deleted file mode 100644 index 3126bb120..000000000 --- a/utils/devices.go +++ /dev/null @@ -1,1165 +0,0 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. - -package utils - -import ( - "bytes" - "encoding/hex" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/spf13/afero" - "golang.org/x/net/context" - - "github.com/netapp/trident/internal/fiji" - . "github.com/netapp/trident/logging" - "github.com/netapp/trident/utils/durations" - "github.com/netapp/trident/utils/errors" - "github.com/netapp/trident/utils/iscsi" - "github.com/netapp/trident/utils/models" -) - -// LUKS2 requires ~16MiB for overhead. Default to 18MiB just in case. -const LUKSMetadataSize = 18874368 - -const ( - luksDevicePrefix = "luks-" - devicesRemovalMaxWaitTime = 5 * time.Second -) - -var ( - beforeFlushMultipathDevice = fiji.Register("beforeFlushMultipathDevice", "devices") - beforeFlushDevice = fiji.Register("beforeFlushDevice", "devices") - beforeRemoveFile = fiji.Register("beforeRemoveFile", "devices") - - LuksCloseDurations = durations.TimeDuration{} - - osFs = afero.NewOsFs() -) - -// waitForDevice accepts a device name and checks if it is present -func waitForDevice(ctx context.Context, device string) error { - fields := LogFields{"device": device} - Logc(ctx).WithFields(fields).Debug(">>>> devices.waitForDevice") - defer Logc(ctx).WithFields(fields).Debug("<<<< devices.waitForDevice") - - exists, err := PathExists(device) - if !exists || err != nil { - return errors.New("device not yet present") - } else { - Logc(ctx).WithField("device", device).Debug("Device found.") - } - return nil -} - -// flushDevice flushes any outstanding I/O to all paths to a device. -func flushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, force bool) error { - Logc(ctx).Debug(">>>> devices.flushDevice") - defer Logc(ctx).Debug("<<<< devices.flushDevice") - - for _, device := range deviceInfo.Devices { - err := flushOneDevice(ctx, iscsi.DevPrefix+device) - if err != nil && !force { - // Return error only if this is a standalone device, i.e. no multipath device is present for this device. - // If a multipath device exists, then it should be flushed before flushing the device, - // hence ignore the error for this device. - if deviceInfo.MultipathDevice == "" { - return err - } - } - } - - return nil -} - -// ensureDeviceReadable reads first 4 KiBs of the device to ensures it is readable -func ensureDeviceReadable(ctx context.Context, device string) error { - Logc(ctx).WithField("device", device).Debug(">>>> devices.ensureDeviceReadable") - defer Logc(ctx).Debug("<<<< devices.ensureDeviceReadable") - - args := []string{"if=" + device, "bs=4096", "count=1", "status=none"} - out, err := command.ExecuteWithTimeout(ctx, "dd", deviceOperationsTimeout, false, args...) - if err != nil { - Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("failed to read the device") - return err - } - - // Ensure 4KiB of data read - if len(out) != 4096 { - Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("read number of bytes not 4KiB") - return fmt.Errorf("did not read 4KiB bytes from the device %v, instead read %d bytes", device, len(out)) - } - - return nil -} - -// isDeviceUnformatted reads first 2 MiBs of the device to identify if it is unformatted and contains all zeros -func isDeviceUnformatted(ctx context.Context, device string) (bool, error) { - Logc(ctx).WithField("device", device).Debug(">>>> devices.isDeviceUnformatted") - defer Logc(ctx).Debug("<<<< devices.isDeviceUnformatted") - - args := []string{"if=" + device, "bs=4096", "count=512", "status=none"} - out, err := command.ExecuteWithTimeout(ctx, "dd", deviceOperationsTimeout, false, args...) - if err != nil { - Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("failed to read the device") - return false, err - } - - // Ensure 2MiB of data read - if len(out) != 2097152 { - Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("read number of bytes not 2MiB") - return false, fmt.Errorf("did not read 2MiB bytes from the device %v, instead read %d bytes; unable to "+ - "ensure if the device is actually unformatted", device, len(out)) - } - - Logc(ctx).WithField("device", device).Debug("Verified correct number of bytes read.") - - // Ensure all zeros - if outWithoutZeros := bytes.Trim(out, "\x00"); len(outWithoutZeros) != 0 { - Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("device contains non-zero values") - return false, nil - } - - Logc(ctx).WithFields(LogFields{"device": device}).Info("Device is unformatted.") - - return true, nil -} - -// removeDevice tells Linux that a device will be removed. -func removeDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, ignoreErrors bool) error { - Logc(ctx).Debug(">>>> devices.removeDevice") - defer Logc(ctx).Debug("<<<< devices.removeDevice") - - var ( - f *os.File - err error - ) - - listAllISCSIDevices(ctx) - for _, deviceName := range deviceInfo.Devices { - - filename := fmt.Sprintf(chrootPathPrefix+"/sys/block/%s/device/delete", deviceName) - if f, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0o200); err != nil { - Logc(ctx).WithField("file", filename).Warning("Could not open file for writing.") - if ignoreErrors { - continue - } - return err - } - - if err := beforeRemoveFile.Inject(); err != nil { - return err - } - if written, err := f.WriteString("1"); err != nil { - Logc(ctx).WithFields(LogFields{"file": filename, "error": err}).Warning("Could not write to file.") - f.Close() - if ignoreErrors { - continue - } - return err - } else if written == 0 { - Logc(ctx).WithField("file", filename).Warning("No data written to file.") - f.Close() - if ignoreErrors { - continue - } - return errors.New("too few bytes written to sysfs file") - } - - f.Close() - - Logc(ctx).WithField("scanFile", filename).Debug("Invoked device delete.") - } - listAllISCSIDevices(ctx) - - return nil -} - -// waitForDevicesRemoval waits for devices to be removed from the system. -func waitForDevicesRemoval(ctx context.Context, osFs afero.Fs, devicePathPrefix string, deviceNames []string, - maxWaitTime time.Duration, -) error { - startTime := time.Now() - for time.Since(startTime) < maxWaitTime { - anyExist := false - for _, device := range deviceNames { - path := filepath.Join(devicePathPrefix, device) - if _, err := osFs.Stat(path); !os.IsNotExist(err) { - anyExist = true - break - } - } - if !anyExist { - return nil - } - time.Sleep(50 * time.Millisecond) - } - - Logc(ctx).WithField("devices", deviceNames).Debug("Timed out waiting for devices to be removed.") - return errors.TimeoutError("timed out waiting for devices to be removed") -} - -// canFlushMultipathDevice determines whether device can be flushed. -// 1. Check the health of path by executing 'multipath -C ' -// 2. If no error, return nil. -// 3. Else, error or 'no usable paths found' -// Check for maxFlushWaitDuration expired, if expired return TimeoutError. -// else, return FlushError. -func canFlushMultipathDevice(ctx context.Context, devicePath string) error { - Logc(ctx).WithField("device", devicePath).Debug(">>>> devices.canFlushMultipathDevice") - defer Logc(ctx).Debug("<<<< devices.canFlushMultipathDevice") - - out, err := command.ExecuteWithTimeout(ctx, "multipath", deviceOperationsTimeout, true, "-C", devicePath) - if err == nil { - delete(iSCSIVolumeFlushExceptions, devicePath) - return nil - } - - outString := string(out) - Logc(ctx).WithFields(LogFields{ - "error": err, - "device": devicePath, - "output": outString, - }).Error("Flush pre-check failed for the device.") - - if !strings.Contains(outString, "no usable paths found") { - return errors.ISCSIDeviceFlushError("multipath device is not ready for flush") - } - - // Apply timeout only for the case LUN is made offline or deleted, - // i.e., "no usable paths found" is returned on health check of the path. - if iSCSIVolumeFlushExceptions[devicePath].IsZero() { - iSCSIVolumeFlushExceptions[devicePath] = time.Now() - } else { - elapsed := time.Since(iSCSIVolumeFlushExceptions[devicePath]) - if elapsed > iSCSIMaxFlushWaitDuration { - Logc(ctx).WithFields( - LogFields{ - "device": devicePath, - "elapsed": elapsed, - "maxWait": iSCSIMaxFlushWaitDuration, - }).Debug("Volume is not safe to remove, but max flush wait time is expired, skip flush.") - delete(iSCSIVolumeFlushExceptions, devicePath) - return errors.TimeoutError(fmt.Sprintf("Max flush wait time expired. Elapsed: %v", elapsed)) - } - } - - return errors.ISCSIDeviceFlushError("multipath device is unavailable") -} - -// multipathFlushDevice invokes the 'multipath' commands to flush paths for a single device. -func multipathFlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo) error { - Logc(ctx).WithField("device", deviceInfo.MultipathDevice).Debug(">>>> devices.multipathFlushDevice") - defer Logc(ctx).Debug("<<<< devices.multipathFlushDevice") - - if deviceInfo.MultipathDevice == "" { - return nil - } - - devicePath := iscsi.DevPrefix + deviceInfo.MultipathDevice - - deviceErr := canFlushMultipathDevice(ctx, devicePath) - if deviceErr != nil { - if errors.IsISCSIDeviceFlushError(deviceErr) { - Logc(ctx).WithFields( - LogFields{ - "device": devicePath, - }).WithError(deviceErr).Debug("Flush failed.") - return deviceErr - } - if errors.IsTimeoutError(deviceErr) { - Logc(ctx).WithFields(LogFields{ - "device": devicePath, - "lun": deviceInfo.LUN, - "host": deviceInfo.Host, - }).WithError(deviceErr).Debug("Flush timed out.") - return deviceErr - } - } - - err := flushOneDevice(ctx, devicePath) - if err != nil { - // Ideally this should not be the case, otherwise, we may need - // to add more checks in canFlushMultipathDevice() - return err - } - - if err = RemoveMultipathDeviceMapping(ctx, devicePath); err != nil { - Logc(ctx).WithFields(LogFields{ - "device": devicePath, - "lun": deviceInfo.LUN, - "host": deviceInfo.Host, - }).WithError(deviceErr).Debug("Error during multipath flush.") - return err - } - return nil -} - -// GetMountedISCSIDevices returns a list of iSCSI devices that are *mounted* on this host. -func GetMountedISCSIDevices(ctx context.Context) ([]*models.ScsiDeviceInfo, error) { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - Logc(ctx).Debug(">>>> devices.GetMountedISCSIDevices") - defer Logc(ctx).Debug("<<<< devices.GetMountedISCSIDevices") - - procSelfMountinfo, err := mountClient.ListProcMountinfo() - if err != nil { - return nil, err - } - - // Get a list of all mounted /dev devices - mountedDevices := make([]string, 0) - for _, procMount := range procSelfMountinfo { - - hasDevMountSourcePrefix := strings.HasPrefix(procMount.MountSource, iscsi.DevPrefix) - hasPvcMountPoint := strings.Contains(procMount.MountPoint, "/pvc-") - - if !hasPvcMountPoint { - continue - } - - var mountedDevice string - // Resolve any symlinks to get the real device - if hasDevMountSourcePrefix { - device, err := filepath.EvalSymlinks(procMount.MountSource) - if err != nil { - Logc(ctx).Error(err) - continue - } - mountedDevice = strings.TrimPrefix(device, iscsi.DevPrefix) - } else { - mountedDevice = strings.TrimPrefix(procMount.Root, "/") - } - - mountedDevices = append(mountedDevices, mountedDevice) - } - - // Get all known iSCSI devices - iscsiDevices, err := GetISCSIDevices(ctx, false) - if err != nil { - return nil, err - } - - mountedISCSIDevices := make([]*models.ScsiDeviceInfo, 0) - - // For each mounted device, look for a matching iSCSI device - for _, mountedDevice := range mountedDevices { - iSCSIDeviceLoop: - for _, iscsiDevice := range iscsiDevices { - // First look for a multipath device match - if mountedDevice == iscsiDevice.MultipathDevice { - mountedISCSIDevices = append(mountedISCSIDevices, iscsiDevice) - break iSCSIDeviceLoop - - } else { - // Then look for a slave device match - for _, iscsiSlaveDevice := range iscsiDevice.Devices { - if mountedDevice == iscsiSlaveDevice { - mountedISCSIDevices = append(mountedISCSIDevices, iscsiDevice) - break iSCSIDeviceLoop - } - } - } - } - } - - for _, md := range mountedISCSIDevices { - Logc(ctx).WithFields(LogFields{ - "host": md.Host, - "lun": md.LUN, - "devices": md.Devices, - "multipathDevice": md.MultipathDevice, - "iqn": md.IQN, - }).Debug("Found mounted iSCSI device.") - } - - return mountedISCSIDevices, nil -} - -// GetISCSIDevices returns a list of iSCSI devices that are attached to (but not necessarily mounted on) this host. -func GetISCSIDevices(ctx context.Context, getCredentials bool) ([]*models.ScsiDeviceInfo, error) { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - Logc(ctx).Debug(">>>> devices.GetISCSIDevices") - defer Logc(ctx).Debug("<<<< devices.GetISCSIDevices") - - devices := make([]*models.ScsiDeviceInfo, 0) - hostSessionMapCache := make(map[string]map[int]int) - - // Start by reading the sessions from /sys/class/iscsi_session - sysPath := chrootPathPrefix + "/sys/class/iscsi_session/" - sessionDirs, err := os.ReadDir(sysPath) - if err != nil { - Logc(ctx).WithField("error", err).Errorf("Could not read %s", sysPath) - return nil, err - } - - // Loop through each of the iSCSI sessions - for _, sessionDir := range sessionDirs { - - var sessionNumber int - var iscsiChapInfo models.IscsiChapInfo - sessionName := sessionDir.Name() - - if !strings.HasPrefix(sessionName, "session") { - continue - } else if sessionNumber, err = strconv.Atoi(strings.TrimPrefix(sessionName, "session")); err != nil { - Logc(ctx).WithField("session", sessionName).Error("Could not parse session number") - return nil, err - } - - // Find the target IQN and Credentials from the session at /sys/class/iscsi_session/sessionXXX/targetname - sessionPath := sysPath + sessionName - sessionFiles := map[string]string{"targetname": "targetIQN"} - if getCredentials { - sessionFiles["username"] = "IscsiUsername" - sessionFiles["username_in"] = "IscsiTargetUsername" - sessionFiles["password"] = "IscsiInitiatorSecret" - sessionFiles["password_in"] = "IscsiTargetSecret" - } - - sessionValues := make(map[string]string, len(sessionFiles)) - for file, value := range sessionFiles { - path := sessionPath + "/" + file - fileBytes, err := os.ReadFile(path) - if err != nil { - Logc(ctx).WithFields(LogFields{ - "path": path, - "error": err, - }).Errorf("Could not read %v file", file) - return nil, err - } - - // When CHAP not in use instead of empty - // credentials they are "(null)" in sysfs - fileContent := strings.TrimSpace(string(fileBytes)) - if fileContent == "(null)" { - fileContent = "" - } - - sessionValues[value] = fileContent - } - - targetIQN := sessionValues["targetIQN"] - - if getCredentials { - iscsiChapInfo = models.IscsiChapInfo{ - IscsiUsername: sessionValues["IscsiUsername"], - IscsiInitiatorSecret: sessionValues["IscsiInitiatorSecret"], - IscsiTargetUsername: sessionValues["IscsiTargetUsername"], - IscsiTargetSecret: sessionValues["IscsiTargetSecret"], - } - - if iscsiChapInfo != (models.IscsiChapInfo{}) { - iscsiChapInfo.UseCHAP = true - } - } - - Logc(ctx).WithFields(LogFields{ - "targetIQN": targetIQN, - "sessionName": sessionName, - }).Debug("Found iSCSI session / target IQN.") - - // Find the one target at /sys/class/iscsi_session/sessionXXX/device/targetHH:BB:DD (host:bus:device) - sessionDevicePath := sessionPath + "/device/" - targetDirs, err := os.ReadDir(sessionDevicePath) - if err != nil { - Logc(ctx).WithField("error", err).Errorf("Could not read %s", sessionDevicePath) - return nil, err - } - - // Get the one target directory - hostBusDeviceName := "" - targetDirName := "" - for _, targetDir := range targetDirs { - - targetDirName = targetDir.Name() - - if strings.HasPrefix(targetDirName, "target") { - hostBusDeviceName = strings.TrimPrefix(targetDirName, "target") - break - } - } - - if hostBusDeviceName == "" { - Logc(ctx).Warningf("Could not find a host:bus:device directory at %s", sessionDevicePath) - continue - } - - sessionDeviceHBDPath := sessionDevicePath + targetDirName + "/" - - Logc(ctx).WithFields(LogFields{ - "hbdPath": sessionDeviceHBDPath, - "hbdName": hostBusDeviceName, - }).Debug("Found host/bus/device path.") - - // Find the devices at /sys/class/iscsi_session/sessionXXX/device/targetHH:BB:DD/HH:BB:DD:LL (host:bus:device:lun) - hostBusDeviceLunDirs, err := os.ReadDir(sessionDeviceHBDPath) - if err != nil { - Logc(ctx).WithField("error", err).Errorf("Could not read %s", sessionDeviceHBDPath) - return nil, err - } - - for _, hostBusDeviceLunDir := range hostBusDeviceLunDirs { - - hostBusDeviceLunDirName := hostBusDeviceLunDir.Name() - if !strings.HasPrefix(hostBusDeviceLunDirName, hostBusDeviceName) { - continue - } - - sessionDeviceHBDLPath := sessionDeviceHBDPath + hostBusDeviceLunDirName + "/" - - Logc(ctx).WithFields(LogFields{ - "hbdlPath": sessionDeviceHBDLPath, - "hbdlName": hostBusDeviceLunDirName, - }).Debug("Found host/bus/device/LUN path.") - - hbdlValues := strings.Split(hostBusDeviceLunDirName, ":") - if len(hbdlValues) != 4 { - Logc(ctx).Errorf("Could not parse values from %s", hostBusDeviceLunDirName) - return nil, err - } - - hostNum := hbdlValues[0] - busNum := hbdlValues[1] - deviceNum := hbdlValues[2] - lunNum := hbdlValues[3] - - blockPath := sessionDeviceHBDLPath + "/block/" - - // Find the block device at /sys/class/iscsi_session/sessionXXX/device/targetHH:BB:DD/HH:BB:DD:LL/block - blockDeviceDirs, err := os.ReadDir(blockPath) - if err != nil { - Logc(ctx).WithField("error", err).Errorf("Could not read %s", blockPath) - return nil, err - } - - for _, blockDeviceDir := range blockDeviceDirs { - - blockDeviceName := blockDeviceDir.Name() - - Logc(ctx).WithField("blockDeviceName", blockDeviceName).Debug("Found block device.") - - // Find multipath device, if any - var slaveDevices []string - multipathDevice := findMultipathDeviceForDevice(ctx, blockDeviceName) - if multipathDevice != "" { - slaveDevices = findDevicesForMultipathDevice(ctx, multipathDevice) - } else { - slaveDevices = []string{blockDeviceName} - } - - // Get the host/session map, using a cached value if available - hostSessionMap, ok := hostSessionMapCache[targetIQN] - if !ok { - hostSessionMap = IscsiUtils.GetISCSIHostSessionMapForTarget(ctx, targetIQN) - hostSessionMapCache[targetIQN] = hostSessionMap - } - - Logc(ctx).WithFields(LogFields{ - "host": hostNum, - "lun": lunNum, - "devices": slaveDevices, - "multipathDevice": multipathDevice, - "iqn": targetIQN, - "sessionNumber": sessionNumber, - "CHAPInUse": iscsiChapInfo.UseCHAP, - "hostSessionMap": hostSessionMap, - }).Debug("Found iSCSI device.") - - device := &models.ScsiDeviceInfo{ - Host: hostNum, - Channel: busNum, - Target: deviceNum, - LUN: lunNum, - Devices: slaveDevices, - MultipathDevice: multipathDevice, - IQN: targetIQN, - SessionNumber: sessionNumber, - CHAPInfo: iscsiChapInfo, - } - - devices = append(devices, device) - } - } - } - - return devices, nil -} - -// findDevicesForMultipathDevice finds the constituent devices for a devicemapper parent device like /dev/dm-0. -func findDevicesForMultipathDevice(ctx context.Context, device string) []string { - Logc(ctx).WithField("device", device).Debug(">>>> devices.findDevicesForMultipathDevice") - defer Logc(ctx).WithField("device", device).Debug("<<<< devices.findDevicesForMultipathDevice") - - devices := make([]string, 0) - - slavesDir := chrootPathPrefix + "/sys/block/" + device + "/slaves" - if dirs, err := os.ReadDir(slavesDir); err == nil { - for _, f := range dirs { - name := f.Name() - if strings.HasPrefix(name, "sd") { - devices = append(devices, name) - } - } - } - - if len(devices) == 0 { - Logc(ctx).WithField("device", device).Debug("Could not find devices for multipath device.") - } else { - Logc(ctx).WithFields(LogFields{ - "device": device, - "devices": devices, - }).Debug("Found devices for multipath device.") - } - - return devices -} - -// compareWithPublishedDevicePath verifies that published path matches the discovered device path -func compareWithPublishedDevicePath( - ctx context.Context, publishInfo *models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, -) (bool, error) { - isProbablyGhostDevice := false - discoverMpath := strings.TrimPrefix(deviceInfo.MultipathDevice, iscsi.DevPrefix) - publishedMpath := strings.TrimPrefix(publishInfo.DevicePath, iscsi.DevPrefix) - - if discoverMpath != publishedMpath { - // If this is the case, a wrong multipath device has been identified. - // Reset the Multipath device and disks - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "discoveredMultipathDevice": discoverMpath, - "publishedMultipathDevice": publishedMpath, - }).Debug("Discovered multipath device may not be correct.") - - deviceInfo.MultipathDevice = strings.TrimPrefix(publishedMpath, iscsi.DevPrefix) - deviceInfo.Devices = []string{} - - // Get Device based on the multipath value at the same time identify if it is a ghost device. - devices, err := IscsiUtils.GetMultipathDeviceDisks(ctx, deviceInfo.MultipathDevice) - if err != nil { - return false, fmt.Errorf("failed to verify multipath disks for '%v'; %v ", - deviceInfo.MultipathDevice, err) - } - - isProbablyGhostDevice = devices == nil || len(devices) == 0 - if isProbablyGhostDevice { - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "multipathDevice": deviceInfo.MultipathDevice, - }).Debug("Multipath device may be a ghost device.") - } else { - deviceInfo.Devices = devices - } - - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "multipathDevice": deviceInfo.MultipathDevice, - "devices": deviceInfo.Devices, - }).Debug("Updated Multipath device and devices.") - } else { - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "publishedMultipathDevice": publishedMpath, - "discoveredMultipathDevice": discoverMpath, - "devices": deviceInfo.Devices, - }).Debug("Discovered multipath device is valid.") - } - - return isProbablyGhostDevice, nil -} - -// compareWithPublishedSerialNumber verifies that device serial number matches the discovered LUNs -func compareWithPublishedSerialNumber( - ctx context.Context, publishInfo *models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, -) (bool, error) { - isProbablyGhostDevice := false - lunSerialCheckPassed := false - - for _, path := range deviceInfo.DevicePaths { - serial, err := getLunSerial(ctx, path) - if err != nil { - // LUN either isn't scanned yet, or this kernel - // doesn't support VPD page 80 in sysfs. Assume - // correctness and move on - Logc(ctx).WithError(err).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "path": path, - }).Error("LUN serial check skipped.") - continue - } - - lunSerialCheckPassed = serial != publishInfo.IscsiLunSerial - if lunSerialCheckPassed { - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "path": path, - }).Error("LUN serial check failed.") - break - } - } - - // It means the multipath device found was wrong - if !lunSerialCheckPassed { - - // Get Device based on the serial number and at the same time identify if it is a ghost device. - // Multipath UUID contains LUN serial in hex format - lunSerialHex := hex.EncodeToString([]byte(publishInfo.IscsiLunSerial)) - multipathDevice, err := IscsiUtils.GetMultipathDeviceBySerial(ctx, lunSerialHex) - if err != nil { - return false, fmt.Errorf("failed to verify multipath device for serial '%v'; %v ", - publishInfo.IscsiLunSerial, err) - } - - deviceInfo.MultipathDevice = strings.TrimPrefix(multipathDevice, iscsi.DevPrefix) - - // Get Device based on the multipath value at the same time identify if it is a ghost device. - devices, err := IscsiUtils.GetMultipathDeviceDisks(ctx, multipathDevice) - if err != nil { - return false, fmt.Errorf("failed to verify multipath disks for '%v', "+ - "serial '%v'; %v", multipathDevice, publishInfo.IscsiLunSerial, err) - } - - isProbablyGhostDevice = devices == nil || len(devices) == 0 - if isProbablyGhostDevice { - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "multipathDevice": multipathDevice, - }).Debug("Multipath device may be a ghost device.") - } else { - deviceInfo.Devices = devices - } - } - - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "multipathDevice": deviceInfo.MultipathDevice, - "devices": deviceInfo.Devices, - }).Debug("Discovered multipath device and devices have valid serial number.") - - return isProbablyGhostDevice, nil -} - -// compareWithAllPublishInfos comparing all publications (allPublishInfos) for -// LUN number uniqueness, if more than one publication exists with the same LUN number -// then it indicates a larger problem that user needs to manually fix -func compareWithAllPublishInfos( - ctx context.Context, publishInfo *models.VolumePublishInfo, - allPublishInfos []models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, -) error { - // During unstaging at least 1 publish info should exist else - // there is some issue on the node. - if len(allPublishInfos) < 1 { - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - }).Debug("Missing all the publish infos; re-requesting.") - - return errors.ISCSISameLunNumberError(fmt.Sprintf( - "failed to verify multipath device '%v' with LUN number '%v' due to missing publish infos", - deviceInfo.MultipathDevice, publishInfo.IscsiLunNumber)) - } - - // Identify if multiple publishInfos for a given targetIQN have the same LUN Number - var count int - for _, info := range allPublishInfos { - if publishInfo.IscsiLunNumber == info.IscsiLunNumber && publishInfo.IscsiTargetIQN == info.IscsiTargetIQN { - count++ - } - } - - if count > 1 { - listAllISCSIDevices(ctx) - - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "count": count, - }).Error("Found multiple publish infos with same LUN ID.") - - return fmt.Errorf("found multiple publish infos with same LUN ID '%d'; user need to correct the publish"+ - " information by including the missing 'devicePath' based on `multipath -ll` output", - publishInfo.IscsiLunNumber) - } - - Logc(ctx).WithFields(LogFields{ - "lun": publishInfo.IscsiLunNumber, - "count": count, - }).Debug("Found publish info with the same LUN ID.") - - return nil -} - -// verifyMultipathDevice verifies that device being removed is correct based on published device path, -// device serial number (if present) or comparing all publications (allPublishInfos) for -// LUN number uniqueness. -func verifyMultipathDevice( - ctx context.Context, publishInfo *models.VolumePublishInfo, - allPublishInfos []models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, -) (bool, error) { - // Ensure a correct multipath device is being discovered. - // Following steps can be performed: - // 1. If DM device is known, compare it with deviceInfo.MultipathDevice - // If no match check if the DM device is a ghost device by checking /sys/block.../slaves and remove it. - // 2. Else if LUN SerialNumber is available, compare it with deviceInfo.Devices Serial Number - // If no match, find a DM device with the matching serial number, - // if a ghost device by checking /sys/block.../uuid then remove it. - // 3. Else if Check all tracking infos to ensure no more than 1 tracking files have the same LUN number. - // If multiple are found, then it requires user intervention. - - if publishInfo.DevicePath != "" { - return compareWithPublishedDevicePath(ctx, publishInfo, deviceInfo) - } else if publishInfo.IscsiLunSerial != "" { - return compareWithPublishedSerialNumber(ctx, publishInfo, deviceInfo) - } - - return false, compareWithAllPublishInfos(ctx, publishInfo, allPublishInfos, deviceInfo) -} - -// PrepareDeviceForRemoval informs Linux that a device will be removed, the function -// also verifies that device being removed is correct based on published device path, -// device serial number (if present) or comparing all publications (allPublishInfos) for -// LUN number uniqueness. -func PrepareDeviceForRemoval( - ctx context.Context, deviceInfo *models.ScsiDeviceInfo, publishInfo *models.VolumePublishInfo, - allPublishInfos []models.VolumePublishInfo, ignoreErrors, force bool, -) (string, error) { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - lunID := int(publishInfo.IscsiLunNumber) - iSCSINodeName := publishInfo.IscsiTargetIQN - - fields := LogFields{ - "lunID": lunID, - "iSCSINodeName": iSCSINodeName, - "chrootPathPrefix": chrootPathPrefix, - } - Logc(ctx).WithFields(fields).Debug(">>>> devices.PrepareDeviceForRemoval") - defer Logc(ctx).WithFields(fields).Debug("<<<< devices.PrepareDeviceForRemoval") - - // CSI Case - // We can't verify a multipath device if we couldn't find it in sysfs. - if publishInfo.IscsiTargetPortal != "" && deviceInfo.MultipathDevice != "" { - _, err := verifyMultipathDevice(ctx, publishInfo, allPublishInfos, deviceInfo) - if err != nil { - return "", err - } - } - - var multipathDevice string - performDeferredDeviceRemoval, err := removeSCSIDevice(ctx, deviceInfo, ignoreErrors, force) - if performDeferredDeviceRemoval && deviceInfo.MultipathDevice != "" { - multipathDevice = iscsi.DevPrefix + deviceInfo.MultipathDevice - Logc(ctx).WithFields(LogFields{ - "lunID": lunID, - "multipathDevice": multipathDevice, - }).Debug("Discovered unmapped multipath device when removing SCSI device.") - } - - return multipathDevice, err -} - -// RemoveMultipathDeviceMapping uses "multipath -f " to flush(remove) unused map. -// Unused maps can happen when Unstage is called on offline/deleted LUN. -func RemoveMultipathDeviceMapping(ctx context.Context, devicePath string) error { - Logc(ctx).WithField("devicePath", devicePath).Debug(">>>> devices.RemoveMultipathDevicemapping") - defer Logc(ctx).Debug("<<<< devices.RemoveMultipathDeviceMapping") - - if devicePath == "" { - return nil - } - if err := beforeFlushMultipathDevice.Inject(); err != nil { - return err - } - out, err := command.ExecuteWithTimeout(ctx, "multipath", 10*time.Second, false, "-f", devicePath) - if err != nil { - pathAlreadyRemoved := strings.Contains(string(out), fmt.Sprintf("'%s' is not a valid argument", devicePath)) - if pathAlreadyRemoved { - Logc(ctx).WithFields(LogFields{ - "output": string(out), - "devicePath": devicePath, - }).WithError(err).Debug("Multipath device already removed.") - } else { - Logc(ctx).WithFields(LogFields{ - "output": string(out), - "devicePath": devicePath, - }).WithError(err).Error("Error encountered in multipath flush(remove) mapping command.") - return fmt.Errorf("failed to flush multipath device: %w", err) - } - } - - return nil -} - -// removeSCSIDevice informs Linux that a device will be removed. The deviceInfo provided only needs -// the devices and multipathDevice fields set. -// IMPORTANT: The unsafe and force arguments have significant ramifications. Setting ignoreErrors=true will cause the -// function to ignore errors, and try to the remove the device even if that results in data loss, data corruption, -// or putting the system into an invalid state. Setting skipFlush=true will cause data loss, as it does not wait for the -// device to flush any remaining data, but this option is provided to avoid an indefinite hang of flush operation in -// case of an end device is in bad state. Setting ignoreErrors=false and skipFlush=false will fail at the first problem -// encountered, so that callers can be assured that a successful return indicates that the device was cleanly removed. -// This is important because while most of the time the top priority is to avoid data -// loss or data corruption, there are times when data loss is unavoidable, or has already -// happened, and in those cases it's better to be able to clean up than to be stuck in an -// endless retry loop. -func removeSCSIDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, ignoreErrors, skipFlush bool) (bool, error) { - listAllISCSIDevices(ctx) - - // Flush multipath device - if !skipFlush { - err := multipathFlushDevice(ctx, deviceInfo) - if err != nil { - if errors.IsTimeoutError(err) { - // Proceed to removeDevice(), ignore any errors. - ignoreErrors = true - } else if !ignoreErrors { - return false, err - } - } - } - - // Flush devices - if !skipFlush { - if err := beforeFlushDevice.Inject(); err != nil { - return false, err - } - err := flushDevice(ctx, deviceInfo, ignoreErrors) - if err != nil && !ignoreErrors { - return false, err - } - } - - // Remove device - err := removeDevice(ctx, deviceInfo, ignoreErrors) - if err != nil && !ignoreErrors { - return false, err - } - - // Wait for device to be removed. Do not ignore errors here as we need the device removed - // for the force removal of the multipath device to succeed. - err = waitForDevicesRemoval(ctx, osFs, iscsi.DevPrefix, deviceInfo.Devices, devicesRemovalMaxWaitTime) - if err != nil { - return false, err - } - - listAllISCSIDevices(ctx) - - // If ignoreErrors was set to true while entering into this function and - // multipathFlushDevice above is executed successfully then multipath device - // mapping would have been removed there. However, we still may attempt - // executing RemoveMultipathDeviceMapping() one more time because of below - // bool return. In RemoveMultipathDeviceMapping() we swallow error for now. - // In case RemoveMultipathDeviceMapping() changes in future to handle error, - // one may need to revisit the below bool ignoreErrors being set on timeout error - // resulting from multipathFlushDevice() call at the start of this function. - return ignoreErrors || skipFlush, nil -} - -type LUKSDevice struct { - rawDevicePath string - mappingName string -} - -func NewLUKSDevice(rawDevicePath, volumeId string) (*LUKSDevice, error) { - luksDeviceName := luksDevicePrefix + volumeId - return &LUKSDevice{rawDevicePath, luksDeviceName}, nil -} - -// MappedDevicePath returns the location of the LUKS device when opened. -func (d *LUKSDevice) MappedDevicePath() string { - return iscsi.DevMapperRoot + d.mappingName -} - -// MappedDeviceName returns the name of the LUKS device when opened. -func (d *LUKSDevice) MappedDeviceName() string { - return d.mappingName -} - -// RawDevicePath returns the original device location, such as the iscsi device or multipath device. Ex: /dev/sdb -func (d *LUKSDevice) RawDevicePath() string { - return d.rawDevicePath -} - -// EnsureFormattedAndOpen ensures the specified device is LUKS formatted and opened. -func (d *LUKSDevice) EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (formatted bool, err error) { - return ensureLUKSDevice(ctx, d, luksPassphrase) -} - -func NewLUKSDeviceFromMappingPath(ctx context.Context, mappingPath, volumeId string) (*LUKSDevice, error) { - rawDevicePath, err := GetUnderlyingDevicePathForLUKSDevice(ctx, mappingPath) - if err != nil { - return nil, fmt.Errorf("could not determine underlying device for LUKS mapping; %v", err) - } - return NewLUKSDevice(rawDevicePath, volumeId) -} - -func ensureLUKSDevice(ctx context.Context, luksDevice models.LUKSDeviceInterface, luksPassphrase string) (bool, error) { - // First check if LUKS device is already opened. This is OK to check even if the device isn't LUKS formatted. - if isOpen, err := luksDevice.IsOpen(ctx); err != nil { - // If the LUKS device isn't found, it means that we need to check if the device is LUKS formatted. - // If it isn't, then we should format it and attempt to open it. - // If any other error occurs, bail out. - if !errors.IsNotFoundError(err) { - Logc(ctx).WithError(err).Error("Could not check if device is an open LUKS device.") - return false, err - } - } else if isOpen { - Logc(ctx).Debug("Device is LUKS formatted and open.") - return true, nil - } - - if err := luksDevice.LUKSFormat(ctx, luksPassphrase); err != nil { - Logc(ctx).WithError(err).Error("Could not LUKS format device.") - return false, fmt.Errorf("could not LUKS format device; %w", err) - } - - // At this point, we should be able to open the device. - if err := luksDevice.Open(ctx, luksPassphrase); err != nil { - // At this point, we couldn't open the LUKS device, but we do know - // the device is LUKS formatted because LUKSFormat didn't fail. - Logc(ctx).WithError(err).Error("Could not open LUKS formatted device.") - return true, fmt.Errorf("could not open LUKS device; %v", err) - } - - Logc(ctx).Debug("Device is LUKS formatted and open.") - return true, nil -} - -func GetLUKSPassphrasesFromSecretMap(secrets map[string]string) (string, string, string, string) { - var luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase string - luksPassphrase = secrets["luks-passphrase"] - luksPassphraseName = secrets["luks-passphrase-name"] - previousLUKSPassphrase = secrets["previous-luks-passphrase"] - previousLUKSPassphraseName = secrets["previous-luks-passphrase-name"] - - return luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase -} - -// EnsureLUKSDeviceMappedOnHost ensures the specified device is LUKS formatted, opened, and has the current passphrase. -func EnsureLUKSDeviceMappedOnHost( - ctx context.Context, luksDevice models.LUKSDeviceInterface, name string, secrets map[string]string, -) (bool, error) { - // Try to Open with current luks passphrase - luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase := GetLUKSPassphrasesFromSecretMap(secrets) - if luksPassphrase == "" { - return false, fmt.Errorf("LUKS passphrase cannot be empty") - } - if luksPassphraseName == "" { - return false, fmt.Errorf("LUKS passphrase name cannot be empty") - } - - Logc(ctx).WithFields(LogFields{ - "volume": name, - "luks-passphrase-name": luksPassphraseName, - }).Info("Opening encrypted volume.") - luksFormatted, err := luksDevice.EnsureFormattedAndOpen(ctx, luksPassphrase) - if err == nil { - return luksFormatted, nil - } - - // If we failed to open, try previous passphrase - if previousLUKSPassphrase == "" { - // Return original error if there is no previous passphrase to use - return luksFormatted, fmt.Errorf("could not open LUKS device; %v", err) - } - if luksPassphrase == previousLUKSPassphrase { - return luksFormatted, fmt.Errorf("could not open LUKS device, previous passphrase matches current") - } - if previousLUKSPassphraseName == "" { - return luksFormatted, fmt.Errorf("could not open LUKS device, no previous passphrase name provided") - } - Logc(ctx).WithFields(LogFields{ - "volume": name, - "luks-passphrase-name": previousLUKSPassphraseName, - }).Info("Opening encrypted volume.") - luksFormatted, err = luksDevice.EnsureFormattedAndOpen(ctx, previousLUKSPassphrase) - if err != nil { - return luksFormatted, fmt.Errorf("could not open LUKS device; %v", err) - } - - return luksFormatted, nil -} - -func ResizeLUKSDevice(ctx context.Context, luksDevicePath, luksPassphrase string) error { - luksDeviceName := filepath.Base(luksDevicePath) - luksDevice := &LUKSDevice{"", luksDeviceName} - return luksDevice.Resize(ctx, luksPassphrase) -} - -// IsLegacyLUKSDevicePath returns true if the device path points to mapped LUKS device instead of mpath device. -func IsLegacyLUKSDevicePath(devicePath string) bool { - return strings.Contains(devicePath, "luks") -} - -func GetLUKSDeviceForMultipathDevice(multipathDevice string) (string, error) { - const luksDeviceUUIDPrefix = "CRYPT-LUKS2" - const luksDeviceUUIDNameOffset = 45 - - dmDevice := strings.TrimSuffix(strings.TrimPrefix(multipathDevice, "/dev/"), "/") - - // Get holder of mpath device - dirents, err := os.ReadDir(fmt.Sprintf("/sys/block/%s/holders/", dmDevice)) - if err != nil { - return "", fmt.Errorf("failed to get holders directory entries; %v", err) - } - - if len(dirents) == 0 { - return "", errors.NotFoundError("no holders found for %s", dmDevice) - } else if len(dirents) > 1 { - return "", fmt.Errorf("%s has %v holders; expected 1", dmDevice, len(dirents)) - } - holder := dirents[0].Name() - - // Verify holder is LUKS device - b, err := os.ReadFile(fmt.Sprintf("/sys/block/%s/dm/uuid", holder)) - if err != nil { - return "", err - } - if !strings.HasPrefix(string(b), luksDeviceUUIDPrefix) { - return "", fmt.Errorf("%s is not a LUKS device", holder) - } - - return iscsi.DevMapperRoot + strings.TrimRight(string(b[luksDeviceUUIDNameOffset:]), "\n"), nil -} - -// ------ TODO remove: temporary functions to bridge the gap while we transition to the new iscsi client ------ - -func GetDeviceInfoForLUN( - ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, isDetachCall bool, -) (*models.ScsiDeviceInfo, error) { - // TODO: check if we require both isDetachCall and needFSType - return iscsiClient.GetDeviceInfoForLUN(ctx, hostSessionMap, lunID, iSCSINodeName, isDetachCall) -} - -func GetDeviceInfoForFCPLUN( - ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, isDetachCall bool, -) (*models.ScsiDeviceInfo, error) { - // TODO: check if we require both isDetachCall and needFSType - deviceInfo, err := fcpClient.GetDeviceInfoForLUN(ctx, lunID, iSCSINodeName, false, isDetachCall) - if err != nil { - return nil, err - } - - return &models.ScsiDeviceInfo{ - Host: deviceInfo.Host, - Channel: deviceInfo.Channel, - Target: deviceInfo.Target, - LUN: deviceInfo.LUN, - Devices: deviceInfo.Devices, - MultipathDevice: deviceInfo.MultipathDevice, - WWNN: deviceInfo.WWNN, - SessionNumber: deviceInfo.SessionNumber, - }, nil -} - -func findMultipathDeviceForDevice(ctx context.Context, device string) string { - return iscsiClient.FindMultipathDeviceForDevice(ctx, device) -} - -func getLunSerial(ctx context.Context, path string) (string, error) { - return iscsiClient.GetLunSerial(ctx, path) -} diff --git a/utils/devices/devices.go b/utils/devices/devices.go new file mode 100644 index 000000000..1c359db72 --- /dev/null +++ b/utils/devices/devices.go @@ -0,0 +1,825 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +//go:generate mockgen -destination=../../mocks/mock_utils/mock_devices/mock_devices_client.go github.com/netapp/trident/utils/devices Devices + +package devices + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/spf13/afero" + "golang.org/x/net/context" + + "github.com/netapp/trident/internal/fiji" + . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/durations" + "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/exec" + "github.com/netapp/trident/utils/models" +) + +const ( + DevPrefix = "/dev/" + DevMapperRoot = "/dev/mapper/" + deviceOperationsTimeout = 5 * time.Second +) + +var ( + // Non-persistent map to maintain flush delays/errors if any, for device path(s). + volumeFlushExceptions = make(map[string]time.Time) + maxFlushWaitDuration = 6 * time.Minute + + duringScanTargetLunAfterFileOpen = fiji.Register("duringISCSIScanTargetLunAfterFileOpen", "iscsi") + beforeFlushMultipathDevice = fiji.Register("beforeFlushMultipathDevice", "devices") + beforeRemoveFile = fiji.Register("beforeRemoveFile", "iscsi") + + LuksCloseDurations = durations.TimeDuration{} +) + +type Devices interface { + FlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, force bool) error + FlushOneDevice(ctx context.Context, devicePath string) error + EnsureDeviceReadable(ctx context.Context, device string) error + IsDeviceUnformatted(ctx context.Context, device string) (bool, error) + ListAllDevices(ctx context.Context) + WaitForDevice(ctx context.Context, device string) error + GetDeviceFSType(ctx context.Context, device string) (string, error) + FindMultipathDeviceForDevice(ctx context.Context, device string) string + FindDevicesForMultipathDevice(ctx context.Context, device string) []string + VerifyMultipathDeviceSize(ctx context.Context, multipathDevice, device string) (int64, bool, error) + GetDiskSize(ctx context.Context, devicePath string) (int64, error) + MultipathFlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo) error + RemoveDevice(ctx context.Context, devices []string, ignoreErrors bool) error + VerifyMultipathDevice( + ctx context.Context, publishInfo *models.VolumePublishInfo, + allPublishInfos []models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, + ) (bool, error) + GetLunSerial(ctx context.Context, path string) (string, error) + GetMultipathDeviceUUID(multipathDevicePath string) (string, error) + GetLUKSDeviceForMultipathDevice(multipathDevice string) (string, error) + ScanTargetLUN(ctx context.Context, lunID int, hosts []int) error + CloseLUKSDevice(ctx context.Context, devicePath string) error + EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error + EnsureLUKSDeviceClosed(ctx context.Context, devicePath string) error + RemoveMultipathDeviceMapping(ctx context.Context, devicePath string) error + WaitForDevicesRemoval(ctx context.Context, devicePathPrefix string, deviceNames []string, + maxWaitTime time.Duration, + ) error +} + +type Client struct { + chrootPathPrefix string + command exec.Command + osFs afero.Afero +} + +func New() *Client { + return NewDetailed(exec.NewCommand(), afero.NewOsFs()) +} + +func NewDetailed(command exec.Command, osFs afero.Fs) *Client { + chrootPathPrefix := "" + if os.Getenv("DOCKER_PLUGIN_MODE") != "" { + chrootPathPrefix = "/host" + } + return &Client{ + chrootPathPrefix: chrootPathPrefix, + command: command, + osFs: afero.Afero{Fs: osFs}, + } +} + +// EnsureDeviceReadable reads first 4 KiBs of the device to ensures it is readable +func (c *Client) EnsureDeviceReadable(ctx context.Context, device string) error { + Logc(ctx).WithField("device", device).Debug(">>>> devices.EnsureDeviceReadable") + defer Logc(ctx).Debug("<<<< devices.EnsureDeviceReadable") + + args := []string{"if=" + device, "bs=4096", "count=1", "status=none"} + out, err := c.command.ExecuteWithTimeout(ctx, "dd", deviceOperationsTimeout, false, args...) + if err != nil { + Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("failed to read the device") + return err + } + + // Ensure 4KiB of data read + if len(out) != 4096 { + Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("read number of bytes not 4KiB") + return fmt.Errorf("did not read 4KiB bytes from the device %v, instead read %d bytes", device, len(out)) + } + + return nil +} + +// IsDeviceUnformatted reads first 2 MiBs of the device to identify if it is unformatted and contains all zeros +func (c *Client) IsDeviceUnformatted(ctx context.Context, device string) (bool, error) { + Logc(ctx).WithField("device", device).Debug(">>>> devices.IsDeviceUnformatted") + defer Logc(ctx).Debug("<<<< devices.IsDeviceUnformatted") + + args := []string{"if=" + device, "bs=4096", "count=512", "status=none"} + out, err := c.command.ExecuteWithTimeout(ctx, "dd", deviceOperationsTimeout, false, args...) + if err != nil { + Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("failed to read the device") + return false, err + } + + // Ensure 2MiB of data read + if len(out) != 2097152 { + Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("read number of bytes not 2MiB") + return false, fmt.Errorf("did not read 2MiB bytes from the device %v, instead read %d bytes; unable to "+ + "ensure if the device is actually unformatted", device, len(out)) + } + + Logc(ctx).WithField("device", device).Debug("Verified correct number of bytes read.") + + // Ensure all zeros + if outWithoutZeros := bytes.Trim(out, "\x00"); len(outWithoutZeros) != 0 { + Logc(ctx).WithFields(LogFields{"error": err, "device": device}).Error("device contains non-zero values") + return false, nil + } + + Logc(ctx).WithFields(LogFields{"device": device}).Info("Device is unformatted.") + + return true, nil +} + +// WaitForDevicesRemoval waits for devices to be removed from the system. +func (c *Client) WaitForDevicesRemoval(ctx context.Context, devicePathPrefix string, deviceNames []string, + maxWaitTime time.Duration, +) error { + startTime := time.Now() + for time.Since(startTime) < maxWaitTime { + anyExist := false + for _, device := range deviceNames { + path := filepath.Join(devicePathPrefix, device) + if _, err := c.osFs.Stat(path); !os.IsNotExist(err) { + anyExist = true + break + } + } + if !anyExist { + return nil + } + time.Sleep(50 * time.Millisecond) + } + + Logc(ctx).WithField("devices", deviceNames).Debug("Timed out waiting for devices to be removed.") + return errors.TimeoutError("timed out waiting for devices to be removed") +} + +// canFlushMultipathDevice determines whether device can be flushed. +// 1. Check the health of path by executing 'multipath -C ' +// 2. If no error, return nil. +// 3. Else, error or 'no usable paths found' +// Check for maxFlushWaitDuration expired, if expired return TimeoutError. +// else, return FlushError. +func (c *Client) canFlushMultipathDevice(ctx context.Context, devicePath string) error { + Logc(ctx).WithField("device", devicePath).Debug(">>>> devices.canFlushMultipathDevice") + defer Logc(ctx).Debug("<<<< devices.canFlushMultipathDevice") + + out, err := c.command.ExecuteWithTimeout(ctx, "multipath", deviceOperationsTimeout, true, "-C", devicePath) + if err == nil { + delete(volumeFlushExceptions, devicePath) + return nil + } + + outString := string(out) + Logc(ctx).WithFields(LogFields{ + "error": err, + "device": devicePath, + "output": outString, + }).Error("Flush pre-check failed for the device.") + + if !strings.Contains(outString, "no usable paths found") { + return errors.ISCSIDeviceFlushError("multipath device is not ready for flush") + } + + // Apply timeout only for the case LUN is made offline or deleted, + // i.e., "no usable paths found" is returned on health check of the path. + if volumeFlushExceptions[devicePath].IsZero() { + volumeFlushExceptions[devicePath] = time.Now() + } else { + elapsed := time.Since(volumeFlushExceptions[devicePath]) + if elapsed > maxFlushWaitDuration { + Logc(ctx).WithFields( + LogFields{ + "device": devicePath, + "elapsed": elapsed, + "maxWait": maxFlushWaitDuration, + }).Debug("Volume is not safe to remove, but max flush wait time is expired, skip flush.") + delete(volumeFlushExceptions, devicePath) + return errors.TimeoutError(fmt.Sprintf("Max flush wait time expired. Elapsed: %v", elapsed)) + } + } + + return errors.ISCSIDeviceFlushError("multipath device is unavailable") +} + +// MultipathFlushDevice invokes the 'multipath' commands to flush paths for a single device. +func (c *Client) MultipathFlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo) error { + Logc(ctx).WithField("device", deviceInfo.MultipathDevice).Debug(">>>> devices.MultipathFlushDevice") + defer Logc(ctx).Debug("<<<< devices.MultipathFlushDevice") + + if deviceInfo.MultipathDevice == "" { + return nil + } + + devicePath := DevPrefix + deviceInfo.MultipathDevice + + deviceErr := c.canFlushMultipathDevice(ctx, devicePath) + if deviceErr != nil { + if errors.IsISCSIDeviceFlushError(deviceErr) { + Logc(ctx).WithFields( + LogFields{ + "device": devicePath, + }).WithError(deviceErr).Debug("Flush failed.") + return deviceErr + } + if errors.IsTimeoutError(deviceErr) { + Logc(ctx).WithFields(LogFields{ + "device": devicePath, + "lun": deviceInfo.LUN, + "host": deviceInfo.Host, + }).WithError(deviceErr).Debug("Flush timed out.") + return deviceErr + } + } + + err := c.FlushOneDevice(ctx, devicePath) + if err != nil { + // Ideally this should not be the case, otherwise, we may need + // to add more checks in canFlushMultipathDevice() + return err + } + + if err = c.RemoveMultipathDeviceMapping(ctx, devicePath); err != nil { + Logc(ctx).WithFields(LogFields{ + "device": devicePath, + "lun": deviceInfo.LUN, + "host": deviceInfo.Host, + }).WithError(deviceErr).Debug("Error during multipath flush.") + return err + } + return nil +} + +// flushDevice flushes any outstanding I/O to all paths to a device. +func (c *Client) FlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, force bool) error { + Logc(ctx).Debug(">>>> devices.FlushDevice") + defer Logc(ctx).Debug("<<<< devices.FlushDevice") + + for _, device := range deviceInfo.Devices { + err := c.FlushOneDevice(ctx, DevPrefix+device) + if err != nil && !force { + // Return error only if this is a standalone device, i.e. no multipath device is present for this device. + // If a multipath device exists, then it should be flushed before flushing the device, + // hence ignore the error for this device. + if deviceInfo.MultipathDevice == "" { + return err + } + } + } + + return nil +} + +// FindDevicesForMultipathDevice finds the constituent devices for a devicemapper parent device like /dev/dm-0. +func (c *Client) FindDevicesForMultipathDevice(ctx context.Context, device string) []string { + Logc(ctx).WithField("device", device).Debug(">>>> devices.findDevicesForMultipathDevice") + defer Logc(ctx).WithField("device", device).Debug("<<<< devices.findDevicesForMultipathDevice") + + devices := make([]string, 0) + + slavesDir := c.chrootPathPrefix + "/sys/block/" + device + "/slaves" + if dirs, err := os.ReadDir(slavesDir); err == nil { + for _, f := range dirs { + name := f.Name() + if strings.HasPrefix(name, "sd") { + devices = append(devices, name) + } + } + } + + if len(devices) == 0 { + Logc(ctx).WithField("device", device).Debug("Could not find devices for multipath device.") + } else { + Logc(ctx).WithFields(LogFields{ + "device": device, + "devices": devices, + }).Debug("Found devices for multipath device.") + } + + return devices +} + +// compareWithPublishedDevicePath verifies that published path matches the discovered device path +func (c *Client) compareWithPublishedDevicePath( + ctx context.Context, publishInfo *models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, +) (bool, error) { + isProbablyGhostDevice := false + discoverMpath := strings.TrimPrefix(deviceInfo.MultipathDevice, DevPrefix) + publishedMpath := strings.TrimPrefix(publishInfo.DevicePath, DevPrefix) + + if discoverMpath != publishedMpath { + // If this is the case, a wrong multipath device has been identified. + // Reset the Multipath device and disks + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "discoveredMultipathDevice": discoverMpath, + "publishedMultipathDevice": publishedMpath, + }).Debug("Discovered multipath device may not be correct.") + + deviceInfo.MultipathDevice = strings.TrimPrefix(publishedMpath, DevPrefix) + deviceInfo.Devices = []string{} + + // Get Device based on the multipath value at the same time identify if it is a ghost device. + devices, err := c.GetMultipathDeviceDisks(ctx, deviceInfo.MultipathDevice) + if err != nil { + return false, fmt.Errorf("failed to verify multipath disks for '%v'; %v ", + deviceInfo.MultipathDevice, err) + } + + isProbablyGhostDevice = devices == nil || len(devices) == 0 + if isProbablyGhostDevice { + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "multipathDevice": deviceInfo.MultipathDevice, + }).Debug("Multipath device may be a ghost device.") + } else { + deviceInfo.Devices = devices + } + + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "multipathDevice": deviceInfo.MultipathDevice, + "devices": deviceInfo.Devices, + }).Debug("Updated Multipath device and devices.") + } else { + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "publishedMultipathDevice": publishedMpath, + "discoveredMultipathDevice": discoverMpath, + "devices": deviceInfo.Devices, + }).Debug("Discovered multipath device is valid.") + } + + return isProbablyGhostDevice, nil +} + +// compareWithPublishedSerialNumber verifies that device serial number matches the discovered LUNs +func (c *Client) compareWithPublishedSerialNumber( + ctx context.Context, publishInfo *models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, +) (bool, error) { + isProbablyGhostDevice := false + lunSerialCheckPassed := false + + for _, path := range deviceInfo.DevicePaths { + serial, err := c.GetLunSerial(ctx, path) + if err != nil { + // LUN either isn't scanned yet, or this kernel + // doesn't support VPD page 80 in sysfs. Assume + // correctness and move on + Logc(ctx).WithError(err).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "path": path, + }).Error("LUN serial check skipped.") + continue + } + + lunSerialCheckPassed = serial != publishInfo.IscsiLunSerial + if lunSerialCheckPassed { + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "path": path, + }).Error("LUN serial check failed.") + break + } + } + + // It means the multipath device found was wrong + if !lunSerialCheckPassed { + + // Get Device based on the serial number and at the same time identify if it is a ghost device. + // Multipath UUID contains LUN serial in hex format + lunSerialHex := hex.EncodeToString([]byte(publishInfo.IscsiLunSerial)) + multipathDevice, err := c.GetMultipathDeviceBySerial(ctx, lunSerialHex) + if err != nil { + return false, fmt.Errorf("failed to verify multipath device for serial '%v'; %v ", + publishInfo.IscsiLunSerial, err) + } + + deviceInfo.MultipathDevice = strings.TrimPrefix(multipathDevice, DevPrefix) + + // Get Device based on the multipath value at the same time identify if it is a ghost device. + devices, err := c.GetMultipathDeviceDisks(ctx, multipathDevice) + if err != nil { + return false, fmt.Errorf("failed to verify multipath disks for '%v', "+ + "serial '%v'; %v", multipathDevice, publishInfo.IscsiLunSerial, err) + } + + isProbablyGhostDevice = devices == nil || len(devices) == 0 + if isProbablyGhostDevice { + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "multipathDevice": multipathDevice, + }).Debug("Multipath device may be a ghost device.") + } else { + deviceInfo.Devices = devices + } + } + + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "multipathDevice": deviceInfo.MultipathDevice, + "devices": deviceInfo.Devices, + }).Debug("Discovered multipath device and devices have valid serial number.") + + return isProbablyGhostDevice, nil +} + +// compareWithAllPublishInfos comparing all publications (allPublishInfos) for +// LUN number uniqueness, if more than one publication exists with the same LUN number +// then it indicates a larger problem that user needs to manually fix +func (c *Client) compareWithAllPublishInfos( + ctx context.Context, publishInfo *models.VolumePublishInfo, + allPublishInfos []models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, +) error { + // During unstaging at least 1 publish info should exist else + // there is some issue on the node. + if len(allPublishInfos) < 1 { + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + }).Debug("Missing all the publish infos; re-requesting.") + + return errors.ISCSISameLunNumberError(fmt.Sprintf( + "failed to verify multipath device '%v' with LUN number '%v' due to missing publish infos", + deviceInfo.MultipathDevice, publishInfo.IscsiLunNumber)) + } + + // Identify if multiple publishInfos for a given targetIQN have the same LUN Number + var count int + for _, info := range allPublishInfos { + if publishInfo.IscsiLunNumber == info.IscsiLunNumber && publishInfo.IscsiTargetIQN == info.IscsiTargetIQN { + count++ + } + } + + if count > 1 { + c.ListAllDevices(ctx) + + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "count": count, + }).Error("Found multiple publish infos with same LUN ID.") + + return fmt.Errorf("found multiple publish infos with same LUN ID '%d'; user need to correct the publish"+ + " information by including the missing 'devicePath' based on `multipath -ll` output", + publishInfo.IscsiLunNumber) + } + + Logc(ctx).WithFields(LogFields{ + "lun": publishInfo.IscsiLunNumber, + "count": count, + }).Debug("Found publish info with the same LUN ID.") + + return nil +} + +// VerifyMultipathDevice verifies that device being removed is correct based on published device path, +// device serial number (if present) or comparing all publications (allPublishInfos) for +// LUN number uniqueness. +func (c *Client) VerifyMultipathDevice( + ctx context.Context, publishInfo *models.VolumePublishInfo, + allPublishInfos []models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo, +) (bool, error) { + // Ensure a correct multipath device is being discovered. + // Following steps can be performed: + // 1. If DM device is known, compare it with deviceInfo.MultipathDevice + // If no match check if the DM device is a ghost device by checking /sys/block.../slaves and remove it. + // 2. Else if LUN SerialNumber is available, compare it with deviceInfo.Devices Serial Number + // If no match, find a DM device with the matching serial number, + // if a ghost device by checking /sys/block.../uuid then remove it. + // 3. Else if Check all tracking infos to ensure no more than 1 tracking files have the same LUN number. + // If multiple are found, then it requires user intervention. + + if publishInfo.DevicePath != "" { + return c.compareWithPublishedDevicePath(ctx, publishInfo, deviceInfo) + } else if publishInfo.IscsiLunSerial != "" { + return c.compareWithPublishedSerialNumber(ctx, publishInfo, deviceInfo) + } + + return false, c.compareWithAllPublishInfos(ctx, publishInfo, allPublishInfos, deviceInfo) +} + +// RemoveMultipathDeviceMapping uses "multipath -f " to flush(remove) unused map. +// Unused maps can happen when Unstage is called on offline/deleted LUN. +func (c *Client) RemoveMultipathDeviceMapping(ctx context.Context, devicePath string) error { + Logc(ctx).WithField("devicePath", devicePath).Debug(">>>> devices.RemoveMultipathDevicemapping") + defer Logc(ctx).Debug("<<<< devices.RemoveMultipathDeviceMapping") + + if devicePath == "" { + return nil + } + if err := beforeFlushMultipathDevice.Inject(); err != nil { + return err + } + out, err := c.command.ExecuteWithTimeout(ctx, "multipath", 10*time.Second, false, "-f", devicePath) + if err != nil { + pathAlreadyRemoved := strings.Contains(string(out), fmt.Sprintf("'%s' is not a valid argument", devicePath)) + if pathAlreadyRemoved { + Logc(ctx).WithFields(LogFields{ + "output": string(out), + "devicePath": devicePath, + }).WithError(err).Debug("Multipath device already removed.") + } else { + Logc(ctx).WithFields(LogFields{ + "output": string(out), + "devicePath": devicePath, + }).WithError(err).Error("Error encountered in multipath flush(remove) mapping command.") + return fmt.Errorf("failed to flush multipath device: %w", err) + } + } + + return nil +} + +// ------ TODO remove: temporary functions to bridge the gap while we transition to the new iscsi client ------ + +//func GetDeviceInfoForLUN( +// ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, isDetachCall bool, +//) (*models.ScsiDeviceInfo, error) { +// // TODO: check if we require both isDetachCall and needFSType +// return iscsiClient.GetDeviceInfoForLUN(ctx, hostSessionMap, lunID, iSCSINodeName, isDetachCall) +//} + +// FindMultipathDeviceForDevice finds the devicemapper parent of a device name like /dev/sdx. +func (c *Client) FindMultipathDeviceForDevice(ctx context.Context, device string) string { + Logc(ctx).WithField("device", device).Debug(">>>> iscsi.findMultipathDeviceForDevice") + defer Logc(ctx).WithField("device", device).Debug("<<<< iscsi.findMultipathDeviceForDevice") + + holdersDir := c.chrootPathPrefix + "/sys/block/" + device + "/holders" + if dirs, err := c.osFs.ReadDir(holdersDir); err == nil { + for _, f := range dirs { + name := f.Name() + if strings.HasPrefix(name, "dm-") { + return name + } + } + } + + Logc(ctx).WithField("device", device).Debug("Could not find multipath device for device.") + return "" +} + +// GetMultipathDeviceDisks find the /sys/block/dmX/slaves/sdX disks. +func (c *Client) GetMultipathDeviceDisks( + ctx context.Context, multipathDevicePath string, +) ([]string, error) { + devices := make([]string, 0) + multipathDevice := strings.TrimPrefix(multipathDevicePath, "/dev/") + + diskPath := c.chrootPathPrefix + fmt.Sprintf("/sys/block/%s/slaves/", multipathDevice) + diskDirs, err := os.ReadDir(diskPath) + if err != nil { + Logc(ctx).WithError(err).Errorf("Could not read %s", diskPath) + return nil, fmt.Errorf("failed to identify multipath device disks; unable to read '%s'", diskPath) + } + + for _, diskDir := range diskDirs { + contentName := diskDir.Name() + if !strings.HasPrefix(contentName, "sd") { + continue + } + + devices = append(devices, contentName) + } + + return devices, nil +} + +// GetMultipathDeviceBySerial find DM device whose UUID /sys/block/dmX/dm/uuid contains serial in hex format. +func (c *Client) GetMultipathDeviceBySerial(ctx context.Context, hexSerial string) (string, error) { + sysPath := c.chrootPathPrefix + "/sys/block/" + + blockDirs, err := os.ReadDir(sysPath) + if err != nil { + Logc(ctx).WithError(err).Errorf("Could not read %s", sysPath) + return "", fmt.Errorf("failed to find multipath device by serial; unable to read '%s'", sysPath) + } + + for _, blockDir := range blockDirs { + dmDeviceName := blockDir.Name() + if !strings.HasPrefix(dmDeviceName, "dm-") { + continue + } + + uuid, err := c.GetMultipathDeviceUUID(dmDeviceName) + if err != nil { + Logc(ctx).WithFields(LogFields{ + "UUID": hexSerial, + "multipathDevice": dmDeviceName, + "err": err, + }).Error("Failed to get UUID of multipath device.") + continue + } + + if strings.Contains(uuid, hexSerial) { + Logc(ctx).WithFields(LogFields{ + "UUID": hexSerial, + "multipathDevice": dmDeviceName, + }).Debug("Found multipath device by UUID.") + return dmDeviceName, nil + } + } + + return "", errors.NotFoundError("no multipath device found") +} + +// GetMultipathDeviceUUID find the /sys/block/dmX/dm/uuid UUID that contains DM device serial in hex format. +func (c *Client) GetMultipathDeviceUUID(multipathDevicePath string) (string, error) { + multipathDevice := strings.TrimPrefix(multipathDevicePath, "/dev/") + + deviceUUIDPath := c.chrootPathPrefix + fmt.Sprintf("/sys/block/%s/dm/uuid", multipathDevice) + + exists, err := PathExists(deviceUUIDPath) + if !exists || err != nil { + return "", errors.NotFoundError("multipath device '%s' UUID not found", multipathDevice) + } + + UUID, err := os.ReadFile(deviceUUIDPath) + if err != nil { + return "", err + } + + return string(UUID), nil +} + +// removeDevice tells Linux that a device will be removed. +func (c *Client) RemoveDevice(ctx context.Context, devices []string, ignoreErrors bool) error { + Logc(ctx).Debug(">>>> devices.removeDevice") + defer Logc(ctx).Debug("<<<< devices.removeDevice") + + var ( + f *os.File + err error + ) + + c.ListAllDevices(ctx) + for _, deviceName := range devices { + + filename := fmt.Sprintf(c.chrootPathPrefix+"/sys/block/%s/device/delete", deviceName) + if f, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0o200); err != nil { + Logc(ctx).WithField("file", filename).Warning("Could not open file for writing.") + if ignoreErrors { + continue + } + return err + } + + if err := beforeRemoveFile.Inject(); err != nil { + return err + } + if written, err := f.WriteString("1"); err != nil { + Logc(ctx).WithFields(LogFields{"file": filename, "error": err}).Warning("Could not write to file.") + f.Close() + if ignoreErrors { + continue + } + return err + } else if written == 0 { + Logc(ctx).WithField("file", filename).Warning("No data written to file.") + f.Close() + if ignoreErrors { + continue + } + return errors.New("too few bytes written to sysfs file") + } + + f.Close() + + Logc(ctx).WithField("scanFile", filename).Debug("Invoked device delete.") + } + c.ListAllDevices(ctx) + + return nil +} + +// GetLunSerial get Linux's idea of what the LUN serial number is +func (c *Client) GetLunSerial(ctx context.Context, path string) (string, error) { + Logc(ctx).WithField("path", path).Debug("Get LUN Serial") + // We're going to read the SCSI VPD page 80 serial number + // information. Linux helpfully provides this through sysfs + // so we don't need to open the device and send the ioctl + // ourselves. + filename := path + "/vpd_pg80" + b, err := c.osFs.ReadFile(filename) + if err != nil { + return "", err + } + if 4 > len(b) || 0x80 != b[1] { + Logc(ctx).WithFields(LogFields{ + "data": b, + }).Error("VPD page 80 format check failed") + return "", fmt.Errorf("malformed VPD page 80 data") + } + length := int(binary.BigEndian.Uint16(b[2:4])) + if len(b) != length+4 { + Logc(ctx).WithFields(LogFields{ + "actual": len(b), + "expected": length + 4, + }).Error("VPD page 80 length check failed") + return "", fmt.Errorf("incorrect length for VPD page 80 serial number") + } + return string(b[4:]), nil +} + +func (c *Client) GetLUKSDeviceForMultipathDevice(multipathDevice string) (string, error) { + const luksDeviceUUIDPrefix = "CRYPT-LUKS2" + const luksDeviceUUIDNameOffset = 45 + + dmDevice := strings.TrimSuffix(strings.TrimPrefix(multipathDevice, "/dev/"), "/") + + // Get holder of mpath device + dirents, err := os.ReadDir(fmt.Sprintf("/sys/block/%s/holders/", dmDevice)) + if err != nil { + return "", err + } + + if len(dirents) == 0 { + return "", errors.NotFoundError("no holders found for %s", dmDevice) + } else if len(dirents) > 1 { + return "", fmt.Errorf("%s has %v holders; expected 1", dmDevice, len(dirents)) + } + holder := dirents[0].Name() + + // Verify holder is LUKS device + b, err := os.ReadFile(fmt.Sprintf("/sys/block/%s/dm/uuid", holder)) + if err != nil { + return "", err + } + if !strings.HasPrefix(string(b), luksDeviceUUIDPrefix) { + return "", fmt.Errorf("%s is not a LUKS device", holder) + } + + return DevMapperRoot + strings.TrimRight(string(b[luksDeviceUUIDNameOffset:]), "\n"), nil +} + +// ScanTargetLUN scans a single LUN or all the LUNs on an iSCSI target to discover it. +// If all the LUNs are to be scanned please pass -1 for lunID. +func (c *Client) ScanTargetLUN(ctx context.Context, lunID int, hosts []int) error { + fields := LogFields{"hosts": hosts, "lunID": lunID} + Logc(ctx).WithFields(fields).Debug(">>>> iscsi.scanTargetLUN") + defer Logc(ctx).WithFields(fields).Debug("<<<< iscsi.scanTargetLUN") + + var ( + f afero.File + err error + ) + + // By default, scan for all the LUNs + scanCmd := "0 0 -" + if lunID >= 0 { + scanCmd = fmt.Sprintf("0 0 %d", lunID) + } + + c.ListAllDevices(ctx) + for _, hostNumber := range hosts { + + filename := fmt.Sprintf(c.chrootPathPrefix+"/sys/class/scsi_host/host%d/scan", hostNumber) + if f, err = c.osFs.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0o200); err != nil { + Logc(ctx).WithField("file", filename).Warning("Could not open file for writing.") + return err + } + + if err = duringScanTargetLunAfterFileOpen.Inject(); err != nil { + return err + } + + if written, err := f.WriteString(scanCmd); err != nil { + Logc(ctx).WithFields(LogFields{"file": filename, "error": err}).Warning("Could not write to file.") + _ = f.Close() + return err + } else if written == 0 { + Logc(ctx).WithField("file", filename).Warning("No data written to file.") + _ = f.Close() + return fmt.Errorf("no data written to %s", filename) + } + + _ = f.Close() + + c.ListAllDevices(ctx) + Logc(ctx).WithFields(LogFields{ + "scanCmd": scanCmd, + "scanFile": filename, + "host": hostNumber, + }).Debug("Invoked SCSI scan for host.") + } + + return nil +} diff --git a/utils/devices/devices_darwin.go b/utils/devices/devices_darwin.go new file mode 100644 index 000000000..6a2ea135b --- /dev/null +++ b/utils/devices/devices_darwin.go @@ -0,0 +1,70 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +// NOTE: This file should only contain functions for handling devices for Darwin flavor + +package devices + +import ( + "golang.org/x/net/context" + + . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/errors" +) + +// flushOneDevice unused stub function +func (c *Client) FlushOneDevice(ctx context.Context, devicePath string) error { + Logc(ctx).Debug(">>>> devices_darwin.FlushOneDevice") + defer Logc(ctx).Debug("<<<< devices_darwin.FlushOneDevice") + return errors.UnsupportedError("FlushOneDevice is not supported for darwin") +} + +func (c *Client) ListAllDevices(ctx context.Context) { + Logc(ctx).Debug(">>>> devices_darwin.ListAllDevices") + defer Logc(ctx).Debug("<<<< devices_darwin.ListAllDevices") + Logc(ctx).Debug("ListAllDevices is not supported for darwin") +} + +func (c *Client) WaitForDevice(ctx context.Context, device string) error { + Logc(ctx).Debug(">>>> devices_darwin.WaitForDevice") + defer Logc(ctx).Debug("<<<< devices_darwin.WaitForDevice") + return errors.UnsupportedError("ListAllDevices is not supported for darwin") +} + +func (c *Client) VerifyMultipathDeviceSize(ctx context.Context, multipathDevice, device string) (int64, bool, error) { + Logc(ctx).Debug(">>>> devices_darwin.VerifyMultipathDeviceSize") + defer Logc(ctx).Debug("<<<< devices_darwin.VerifyMultipathDeviceSize") + return 0, false, errors.UnsupportedError("VerifyMultipathDeviceSize is not supported for darwin") +} + +// GetDiskSize unused stub function +func (c *Client) GetDiskSize(ctx context.Context, _ string) (int64, error) { + Logc(ctx).Debug(">>>> devices_darwin.GetDiskSize") + defer Logc(ctx).Debug("<<<< devices_darwin.GetDiskSize") + return 0, errors.UnsupportedError("GetDiskSize is not supported for darwin") +} + +func (c *Client) EnsureLUKSDeviceClosed(ctx context.Context, luksDevicePath string) error { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + + Logc(ctx).Debug(">>>> devices_darwin.EnsureLUKSDeviceClosed") + defer Logc(ctx).Debug("<<<< devices_darwin.EnsureLUKSDeviceClosed") + return errors.UnsupportedError("EnsureLUKSDeviceClosed is not supported for darwin") +} + +func (c *Client) GetDeviceFSType(ctx context.Context, device string) (string, error) { + Logc(ctx).Debug(">>>> devices_darwin.GetDeviceFSType") + defer Logc(ctx).Debug("<<<< devices_darwin.GetDeviceFSType") + return "", errors.UnsupportedError("GetDeviceFSTypeis not supported for darwin") +} + +func (c *Client) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { + Logc(ctx).Debug(">>>> devices_darwin.EnsureLUKSDeviceClosedWithMaxWaitLimit") + defer Logc(ctx).Debug("<<<< devices_darwin.EnsureLUKSDeviceClosedWithMaxWaitLimit") + return errors.UnsupportedError("EnsureLUKSDeviceClosedWithMaxWaitLimit is not supported for darwin") +} + +func (c *Client) CloseLUKSDevice(ctx context.Context, devicePath string) error { + Logc(ctx).Debug(">>>> devices_darwin.CloseLUKSDevice") + defer Logc(ctx).Debug("<<<< devices_darwin.CloseLUKSDevice") + return errors.UnsupportedError("CloseLUKSDevice is not supported for darwin") +} diff --git a/utils/devices_darwin_test.go b/utils/devices/devices_darwin_test.go similarity index 65% rename from utils/devices_darwin_test.go rename to utils/devices/devices_darwin_test.go index 8436b4e64..33375f6a7 100644 --- a/utils/devices_darwin_test.go +++ b/utils/devices/devices_darwin_test.go @@ -1,20 +1,23 @@ // Copyright 2022 NetApp, Inc. All Rights Reserved. -package utils +package devices import ( "context" "testing" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/exec" ) func TestFlushOneDevice(t *testing.T) { ctx := context.Background() - result := flushOneDevice(ctx, "/test/path") + devices := NewDetailed(exec.NewCommand(), afero.NewMemMapFs()) + result := devices.FlushOneDevice(ctx, "/test/path") assert.Error(t, result, "no error") assert.True(t, errors.IsUnsupportedError(result), "not UnsupportedError") } @@ -22,7 +25,8 @@ func TestFlushOneDevice(t *testing.T) { func TestGetISCSIDiskSize(t *testing.T) { ctx := context.Background() - result, err := getISCSIDiskSize(ctx, "/test/path") + devices := NewDetailed(exec.NewCommand(), afero.NewMemMapFs()) + result, err := devices.GetDiskSize(ctx, "/test/path") assert.Equal(t, result, int64(0), "received disk size") assert.Error(t, err, "no error") assert.True(t, errors.IsUnsupportedError(err), "not UnsupportedError") diff --git a/utils/devices/devices_linux.go b/utils/devices/devices_linux.go new file mode 100644 index 000000000..232e20827 --- /dev/null +++ b/utils/devices/devices_linux.go @@ -0,0 +1,303 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +// NOTE: This file should only contain functions for handling devices for linux flavor + +package devices + +import ( + "fmt" + "os" + "os/exec" + "strings" + "syscall" + "time" + "unsafe" + + "golang.org/x/net/context" + "golang.org/x/sys/unix" + + "github.com/netapp/trident/internal/fiji" + . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/filesystem" +) + +const ( + luksCloseTimeout = 30 * time.Second + luksCypherMode = "aes-xts-plain64" + luksType = "luks2" + + // Return codes for `cryptsetup status` + cryptsetupStatusDeviceDoesExistStatusCode = 0 + cryptsetupStatusDeviceDoesNotExistStatusCode = 4 + + // Return codes for `cryptsetup isLuks` + cryptsetupIsLuksDeviceIsLuksStatusCode = 0 + cryptsetupIsLuksDeviceIsNotLuksStatusCode = 1 + + luksCloseMaxWaitDuration = 2 * time.Minute +) + +var ( + afterLuksClose = fiji.Register("afterLuksClose", "devices_linux") + beforeLuksClose = fiji.Register("beforeLuksClose", "devices_linux") + beforeBlockDeviceFlushBuffer = fiji.Register("beforeBlockDeviceFlushBuffer", "devices_linux") +) + +// FlushOneDevice flushes any outstanding I/O to a disk +func (c *Client) FlushOneDevice(ctx context.Context, devicePath string) error { + fields := LogFields{"rawDevicePath": devicePath} + Logc(ctx).WithFields(fields).Debug(">>>> devices_linux.flushOneDevice") + defer Logc(ctx).WithFields(fields).Debug("<<<< devices_linux.flushOneDevice") + + if err := beforeBlockDeviceFlushBuffer.Inject(); err != nil { + return err + } + + out, err := c.command.ExecuteWithTimeout( + ctx, "blockdev", deviceOperationsTimeout, true, "--flushbufs", devicePath, + ) + if err != nil { + Logc(ctx).WithFields( + LogFields{ + "error": err, + "output": string(out), + "device": devicePath, + }).Debug("blockdev --flushbufs failed.") + return fmt.Errorf("flush device failed for %s : %s", devicePath, err) + } + + return nil +} + +// GetDiskSize queries the current block size in bytes +func (c *Client) GetDiskSize(ctx context.Context, devicePath string) (int64, error) { + fields := LogFields{"rawDevicePath": devicePath} + Logc(ctx).WithFields(fields).Debug(">>>> devices_linux.GetDiskSize") + defer Logc(ctx).WithFields(fields).Debug("<<<< devices_linux.GetDiskSize") + + disk, err := os.Open(devicePath) + if err != nil { + Logc(ctx).Error("Failed to open disk.") + return 0, fmt.Errorf("failed to open disk %s: %s", devicePath, err) + } + defer disk.Close() + + var size int64 + // TODO (vhs): Check if syscall can be avoided. + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, disk.Fd(), unix.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) + if errno != 0 { + err := os.NewSyscallError("ioctl", errno) + Logc(ctx).Error("BLKGETSIZE64 ioctl failed: ", err) + return 0, fmt.Errorf("BLKGETSIZE64 ioctl failed %s: %s", devicePath, err) + } + + return size, nil +} + +// VerifyMultipathDeviceSize compares the size of the DM device with the size +// of a device to ensure correct DM device has the correct size. +func (c *Client) VerifyMultipathDeviceSize( + ctx context.Context, multipathDevice, device string, +) (int64, bool, error) { + deviceSize, err := c.GetDiskSize(ctx, "/dev/"+device) + if err != nil { + return 0, false, err + } + + mpathSize, err := c.GetDiskSize(ctx, "/dev/"+multipathDevice) + if err != nil { + return 0, false, err + } + + if deviceSize != mpathSize { + return deviceSize, false, nil + } + + Logc(ctx).WithFields(LogFields{ + "multipathDevice": multipathDevice, + "device": device, + }).Debug("Multipath device size check passed.") + + return 0, true, nil +} + +// CloseLUKSDevice performs a luksClose on the device at the specified path (example: "/dev/mapper/"). +func (c *Client) CloseLUKSDevice(ctx context.Context, devicePath string) error { + if err := beforeLuksClose.Inject(); err != nil { + return err + } + + output, err := c.command.ExecuteWithTimeoutAndInput( + ctx, "cryptsetup", luksCloseTimeout, true, "", "luksClose", devicePath, + ) + + if err := afterLuksClose.Inject(); err != nil { + return err + } + + if err != nil { + fields := LogFields{"luksDevicePath": devicePath, "output": string(output)} + Logc(ctx).WithFields(fields).WithError(err).Debug("Failed to close LUKS device") + return fmt.Errorf("failed to close LUKS device %s; %w", devicePath, err) + } + + Logc(ctx).WithField("luksDevicePath", devicePath).Debug("Closed LUKS device.") + return nil +} + +// EnsureLUKSDeviceClosed ensures there is no open LUKS device at the specified path (example: "/dev/mapper/"). +func (c *Client) EnsureLUKSDeviceClosed(ctx context.Context, devicePath string) error { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + _, err := c.osFs.Stat(devicePath) + if err == nil { + return c.CloseLUKSDevice(ctx, devicePath) + } else if !os.IsNotExist(err) { + Logc(ctx).WithFields(LogFields{ + "device": devicePath, + "error": err.Error(), + }).Debug("Failed to stat device.") + return fmt.Errorf("could not stat device: %s; %v", devicePath, err) + } + Logc(ctx).WithFields(LogFields{ + "device": devicePath, + }).Debug("LUKS device not found.") + + return nil +} + +func (c *Client) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { + if err := c.EnsureLUKSDeviceClosed(ctx, luksDevicePath); err != nil { + LuksCloseDurations.InitStartTime(luksDevicePath) + elapsed, durationErr := LuksCloseDurations.GetCurrentDuration(luksDevicePath) + if durationErr != nil { + return durationErr + } + if elapsed > luksCloseMaxWaitDuration { + Logc(ctx).WithFields( + LogFields{ + "device": luksDevicePath, + "elapsed": elapsed, + "maxWait": luksDevicePath, + }).Debug("LUKS close max wait time expired, continuing with removal.") + return errors.MaxWaitExceededError(fmt.Sprintf("LUKS close wait time expired. Elapsed: %v", elapsed)) + } + return err + } + return nil +} + +// GetDeviceFSType returns the filesystem for the supplied device. +func (c *Client) GetDeviceFSType(ctx context.Context, device string) (string, error) { + Logc(ctx).WithField("device", device).Debug(">>>> devices.getDeviceFSType") + defer Logc(ctx).Debug("<<<< devices.getDeviceFSType") + + // blkid return status=2 both in case of an unformatted filesystem as well as for the case when it is + // unable to get the filesystem (e.g. IO error), therefore ensure device is available before calling blkid + if err := c.WaitForDevice(ctx, device); err != nil { + return "", fmt.Errorf("could not find device before checking for the filesystem %v; %s.", device, err) + } + + out, err := c.command.ExecuteWithTimeout(ctx, "blkid", 5*time.Second, true, device) + if err != nil { + if errors.IsTimeoutError(err) { + c.ListAllDevices(ctx) + return "", err + } else if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 2 { + // EITHER: Disk device is unformatted. + // OR: For 'blkid', if the specified token (TYPE/PTTYPE, etc) was + // not found, or no (specified) devices could be identified, an + // exit code of 2 is returned. + + Logc(ctx).WithField("device", device).Infof("Could not get FSType for device; err: %v.", err) + return "", nil + } + + Logc(ctx).WithField("device", device).Errorf("Could not determine FSType for device; err: %v.", err) + return "", err + } + + var fsType string + + if strings.Contains(string(out), "TYPE=") { + for _, v := range strings.Split(string(out), " ") { + if strings.Contains(v, "TYPE=") { + fsType = strings.Split(v, "=")[1] + fsType = strings.Replace(fsType, "\"", "", -1) + fsType = strings.TrimSpace(fsType) + } + } + } + + if fsType == "" { + Logc(ctx).WithField("out", string(out)).Errorf("Unable to identify fsType.") + + // Read the device to see if it is in fact formatted + if unformatted, err := c.IsDeviceUnformatted(ctx, device); err != nil { + Logc(ctx).WithFields(LogFields{ + "device": device, + "err": err, + }).Debugf("Unable to identify if the device is not unformatted.") + } else if !unformatted { + Logc(ctx).WithField("device", device).Debugf("Device is not unformatted.") + return filesystem.UnknownFstype, nil + } else { + // If we are here blkid should have not retured exit status 0, we need to retry. + Logc(ctx).WithField("device", device).Errorf("Device is unformatted.") + } + + return "", fmt.Errorf("unable to identify fsType") + } + + return fsType, nil +} + +// ListAllDevices logs info about session and what devices are present +func (c *Client) ListAllDevices(ctx context.Context) { + Logc(ctx).Trace(">>>> devices.ListAllDevices") + defer Logc(ctx).Trace("<<<< devices.ListAllDevices") + // Log information about all the devices + dmLog := make([]string, 0) + sdLog := make([]string, 0) + sysLog := make([]string, 0) + entries, _ := c.osFs.ReadDir(DevPrefix) + for _, entry := range entries { + if strings.HasPrefix(entry.Name(), "dm-") { + dmLog = append(dmLog, entry.Name()) + } + if strings.HasPrefix(entry.Name(), "sd") { + sdLog = append(sdLog, entry.Name()) + } + } + + entries, _ = c.osFs.ReadDir("/sys/block/") + for _, entry := range entries { + sysLog = append(sysLog, entry.Name()) + } + + // TODO: Call this only when verbose logging requires beyond debug level. + // out1, _ := command.ExecuteWithTimeout(ctx, "multipath", deviceOperationsTimeout, true, "-ll") + // out2, _ := execIscsiadmCommand(ctx, "-m", "session") + Logc(ctx).WithFields(LogFields{ + "/dev/dm-*": dmLog, + "/dev/sd*": sdLog, + "/sys/block/*": sysLog, + // "multipath -ll output": string(out1), + // "iscsiadm -m session output": string(out2), + }).Trace("Listing all devices.") +} + +// WaitForDevice accepts a device name and checks if it is present +func (c *Client) WaitForDevice(ctx context.Context, device string) error { + fields := LogFields{"device": device} + Logc(ctx).WithFields(fields).Debug(">>>> devices.waitForDevice") + defer Logc(ctx).WithFields(fields).Debug("<<<< devices.waitForDevice") + + exists, err := PathExists(device) + if !exists || err != nil { + return errors.New("device not yet present") + } else { + Logc(ctx).WithField("device", device).Debug("Device found.") + } + return nil +} diff --git a/utils/devices/devices_linux_test.go b/utils/devices/devices_linux_test.go new file mode 100644 index 000000000..60cbe1a51 --- /dev/null +++ b/utils/devices/devices_linux_test.go @@ -0,0 +1,145 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +// NOTE: This file should only contain functions for handling devices for linux flavor + +package devices + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + + mockexec "github.com/netapp/trident/mocks/mock_utils/mock_exec" + "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/exec" +) + +func mockCryptsetupLuksClose(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCloseTimeout, true, "", "luksClose", gomock.Any(), + ) +} + +func TestEnsureLUKSDeviceClosedWithMaxWaitLimit(t *testing.T) { + osFs := afero.NewMemMapFs() + luksDevicePath := "/dev/mapper/luks-test" + osFs.Create(luksDevicePath) + client := mockexec.NewMockCommand(gomock.NewController(t)) + deviceClient := NewDetailed(client, osFs) + + type testCase struct { + name string + mockSetup func(*mockexec.MockCommand) + expectedError bool + expectedErrType error + } + + testCases := []testCase{ + { + name: "SucceedsWhenDeviceIsClosed", + mockSetup: func(mockCommand *mockexec.MockCommand) { + mockCryptsetupLuksClose(mockCommand).Return([]byte(""), nil) + }, + expectedError: false, + }, + { + name: "FailsBeforeMaxWaitLimit", + mockSetup: func(mockCommand *mockexec.MockCommand) { + mockCryptsetupLuksClose(mockCommand).Return([]byte(""), fmt.Errorf("close error")) + }, + expectedError: true, + expectedErrType: fmt.Errorf("%w", errors.New("")), + }, + { + name: "FailsWithMaxWaitExceededError", + mockSetup: func(mockCommand *mockexec.MockCommand) { + mockCryptsetupLuksClose(mockCommand).Return([]byte(""), fmt.Errorf("close error")) + LuksCloseDurations[luksDevicePath] = time.Now().Add(-luksCloseMaxWaitDuration - time.Second) + }, + expectedError: true, + expectedErrType: errors.MaxWaitExceededError(""), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tc.mockSetup(client) + err := deviceClient.EnsureLUKSDeviceClosedWithMaxWaitLimit(context.TODO(), luksDevicePath) + if tc.expectedError { + assert.Error(t, err) + if tc.expectedErrType != nil { + assert.IsType(t, tc.expectedErrType, err) + } + } else { + assert.NoError(t, err) + } + }) + } +} + +func Test_CloseLUKSDevice(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + deviceClient := NewDetailed(mockCommand, afero.NewMemMapFs()) + + // Setup mock calls and reassign any clients to their mock counterparts. + mockCryptsetupLuksClose(mockCommand).Return([]byte(""), nil) + + err := deviceClient.CloseLUKSDevice(context.Background(), "/dev/sdb") + assert.NoError(t, err) +} + +func TestEnsureLUKSDeviceClosed_DeviceDoesNotExist(t *testing.T) { + deviceClient := NewDetailed(exec.NewCommand(), afero.NewMemMapFs()) + err := deviceClient.EnsureLUKSDeviceClosed(context.Background(), "/dev/mapper/luks-test-dev") + assert.NoError(t, err) +} + +func TestEnsureLUKSDeviceClosed_FailsToDetectDevice(t *testing.T) { + osFs := afero.NewOsFs() + var b strings.Builder + b.Grow(1025) + for i := 0; i < 1025; i++ { + b.WriteByte('a') + } + s := b.String() + deviceClient := NewDetailed(exec.NewCommand(), osFs) + err := deviceClient.EnsureLUKSDeviceClosed(context.Background(), "/dev/mapper/"+s) + assert.Error(t, err) +} + +func TestEnsureLUKSDeviceClosed_FailsToCloseDevice(t *testing.T) { + ctx := context.Background() + devicePath := "/dev/mapper/luks-test-dev" + osFs := afero.NewMemMapFs() + osFs.Create(devicePath) + + deviceClient := NewDetailed(exec.NewCommand(), osFs) + err := deviceClient.EnsureLUKSDeviceClosed(ctx, devicePath) + assert.Error(t, err) +} + +func TestEnsureLUKSDeviceClosed_ClosesDevice(t *testing.T) { + ctx := context.Background() + devicePath := "/dev/mapper/luks-test-dev" + osFs := afero.NewMemMapFs() + osFs.Create(devicePath) + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + // Setup mock calls and reassign any clients to their mock counterparts. + gomock.InOrder( + mockCryptsetupLuksClose(mockCommand).Return([]byte(""), nil), + ) + + deviceClient := NewDetailed(mockCommand, osFs) + err := deviceClient.EnsureLUKSDeviceClosed(ctx, devicePath) + assert.NoError(t, err) +} diff --git a/utils/devices/devices_test.go b/utils/devices/devices_test.go new file mode 100644 index 000000000..0be6b2ac6 --- /dev/null +++ b/utils/devices/devices_test.go @@ -0,0 +1,449 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +package devices + +import ( + "context" + "errors" + "fmt" + "os" + "testing" + "time" + + "github.com/spf13/afero" + "github.com/spf13/afero/mem" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + + mockexec "github.com/netapp/trident/mocks/mock_utils/mock_exec" + tridentError "github.com/netapp/trident/utils/errors" +) + +func TestRemoveMultipathDeviceMapping(t *testing.T) { + mockCommand := mockexec.NewMockCommand(gomock.NewController(t)) + + tests := []struct { + name string + devicePath string + mockReturn []byte + mockError error + expectError bool + }{ + { + name: "Happy Path", + devicePath: "/dev/mock-0", + mockReturn: []byte("mock output"), + mockError: nil, + expectError: false, + }, + { + name: "Blank Device Path", + devicePath: "", + mockReturn: nil, + mockError: nil, + expectError: false, + }, + { + name: "Device does not exist", + devicePath: "/dev/mapper/doesNotExist", + mockReturn: []byte("'/dev/mapper/doesNotExist' is not a valid argument"), + mockError: fmt.Errorf("error"), + expectError: false, + }, + { + name: "Negative case", + devicePath: "/dev/mock-0", + mockReturn: nil, + mockError: fmt.Errorf("error"), + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.devicePath != "" { + mockCommand.EXPECT().ExecuteWithTimeout(gomock.Any(), "multipath", 10*time.Second, false, "-f", tt.devicePath). + Return(tt.mockReturn, tt.mockError) + } + + deviceClient := NewDetailed(mockCommand, afero.NewMemMapFs()) + err := deviceClient.RemoveMultipathDeviceMapping(context.TODO(), tt.devicePath) + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +type aferoWrapper struct { + openFileError error + openFileResponse afero.File + openResponse afero.File + openError error + afero.Fs +} + +type aferoFileWrapper struct { + WriteStringError error + WriteStringCount int + afero.File +} + +func (a *aferoWrapper) OpenFile(_ string, _ int, _ os.FileMode) (afero.File, error) { + return a.openFileResponse, a.openFileError +} + +func (a *aferoWrapper) Open(_ string) (afero.File, error) { + return a.openResponse, a.openError +} + +func (a *aferoFileWrapper) WriteString(_ string) (ret int, err error) { + return a.WriteStringCount, a.WriteStringError +} + +func TestClient_scanTargetLUN(t *testing.T) { + type parameters struct { + assertError assert.ErrorAssertionFunc + getFileSystemUtils func() afero.Fs + } + + const lunID = 0 + const host1 = 1 + const host2 = 2 + + tests := map[string]parameters{ + "scan files not present": { + getFileSystemUtils: func() afero.Fs { + return afero.NewMemMapFs() + }, + assertError: assert.Error, + }, + "error writing to scan files": { + getFileSystemUtils: func() afero.Fs { + memFs := afero.NewMemMapFs() + _, err := memFs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host1)) + assert.NoError(t, err) + _, err = memFs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host2)) + assert.NoError(t, err) + + f := &aferoFileWrapper{ + WriteStringError: errors.New("some error"), + File: mem.NewFileHandle(&mem.FileData{}), + } + + fs := &aferoWrapper{ + openFileResponse: f, + openResponse: f, + Fs: memFs, + } + + return fs + }, + assertError: assert.Error, + }, + "failed to write to scan files": { + getFileSystemUtils: func() afero.Fs { + memFs := afero.NewMemMapFs() + _, err := memFs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host1)) + assert.NoError(t, err) + _, err = memFs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host2)) + assert.NoError(t, err) + + f := &aferoFileWrapper{ + WriteStringCount: 0, + File: mem.NewFileHandle(&mem.FileData{}), + } + + fs := &aferoWrapper{ + openFileResponse: f, + openResponse: f, + Fs: memFs, + } + + return fs + }, + assertError: assert.Error, + }, + "happy path": { + getFileSystemUtils: func() afero.Fs { + fs := afero.NewMemMapFs() + _, err := fs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host1)) + assert.NoError(t, err) + _, err = fs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host2)) + assert.NoError(t, err) + return fs + }, + assertError: assert.NoError, + }, + } + + for name, params := range tests { + t.Run(name, func(t *testing.T) { + client := NewDetailed(nil, afero.Afero{Fs: params.getFileSystemUtils()}) + + err := client.ScanTargetLUN(context.TODO(), lunID, []int{host1, host2}) + if params.assertError != nil { + params.assertError(t, err) + } + }) + } +} + +func vpdpg80SerialBytes(serial string) []byte { + return append([]byte{0, 128, 0, 20}, []byte(serial)...) +} + +func TestClient_getLunSerial(t *testing.T) { + type parameters struct { + getFileSystemUtils func() afero.Fs + expectedResponse string + assertError assert.ErrorAssertionFunc + } + + const devicePath = "/dev/sda" + const vpdpg80Serial = "SYA5GZFJ8G1M905GVH7H" + + tests := map[string]parameters{ + "error reading serial file": { + getFileSystemUtils: func() afero.Fs { + fs := afero.NewMemMapFs() + return fs + }, + expectedResponse: "", + assertError: assert.Error, + }, + "invalid serial in file len < 4 bytes": { + getFileSystemUtils: func() afero.Fs { + fs := afero.NewMemMapFs() + f, err := fs.Create(devicePath + "/vpd_pg80") + assert.NoError(t, err) + _, err = f.Write([]byte("123")) + assert.NoError(t, err) + return fs + }, + expectedResponse: "", + assertError: assert.Error, + }, + "invalid serial bytes[1] != 0x80": { + getFileSystemUtils: func() afero.Fs { + fs := afero.NewMemMapFs() + f, err := fs.Create(devicePath + "/vpd_pg80") + assert.NoError(t, err) + _, err = f.Write([]byte{0x81, 0x00, 0x00, 0x00, 0x00}) + assert.NoError(t, err) + return fs + }, + expectedResponse: "", + assertError: assert.Error, + }, + "invalid serial bad length": { + getFileSystemUtils: func() afero.Fs { + fs := afero.NewMemMapFs() + f, err := fs.Create(devicePath + "/vpd_pg80") + assert.NoError(t, err) + _, err = f.Write([]byte{0x81, 0x80, 0x01, 0x01, 0x02}) + assert.NoError(t, err) + return fs + }, + expectedResponse: "", + assertError: assert.Error, + }, + "happy path": { + getFileSystemUtils: func() afero.Fs { + fs := afero.NewMemMapFs() + f, err := fs.Create(devicePath + "/vpd_pg80") + assert.NoError(t, err) + _, err = f.Write(vpdpg80SerialBytes(vpdpg80Serial)) + assert.NoError(t, err) + return fs + }, + expectedResponse: vpdpg80Serial, + assertError: assert.NoError, + }, + } + + for name, params := range tests { + t.Run(name, func(t *testing.T) { + client := NewDetailed(nil, afero.Afero{Fs: params.getFileSystemUtils()}) + response, err := client.GetLunSerial(context.TODO(), devicePath) + if params.assertError != nil { + params.assertError(t, err) + } + assert.Equal(t, params.expectedResponse, response) + }) + } +} + +// NOTE: Since this is now in the devices package, +// we cannot unit test this without finding a way to mock file's Fd() function. +// Afero does not support this. +//func TestClient_verifyMultipathDeviceSize(t *testing.T) { +// type parameters struct { +// getDevicesClient func(controller *gomock.Controller) Devices +// getFs func(controller *gomock.Controller) afero.Fs +// assertError assert.ErrorAssertionFunc +// assertValid assert.BoolAssertionFunc +// expectedDeviceSize int64 +// } +// +// const deviceName = "sda" +// const multipathDeviceName = "dm-0" +// +// tests := map[string]parameters{ +// "error getting device size": { +// getDevicesClient: func(controller *gomock.Controller) Devices { +// mockDevices := mock_devices.NewMockDevices(controller) +// mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(0), +// errors.New("some error")) +// return mockDevices +// }, +// assertError: assert.Error, +// assertValid: assert.False, +// expectedDeviceSize: 0, +// }, +// "error getting multipath device size": { +// getDevicesClient: func(controller *gomock.Controller) Devices { +// mockDevices := mock_devices.NewMockDevices(controller) +// mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(1), nil) +// mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(0), +// errors.New("some error")) +// return mockDevices +// }, +// assertError: assert.Error, +// assertValid: assert.False, +// expectedDeviceSize: 0, +// }, +// "device size != multipath device size": { +// getDevicesClient: func(controller *gomock.Controller) Devices { +// mockDevices := mock_devices.NewMockDevices(controller) +// mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(1), nil) +// mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(0), nil) +// return mockDevices +// }, +// assertError: assert.NoError, +// assertValid: assert.False, +// expectedDeviceSize: 1, +// }, +// "happy path": { +// getDevicesClient: func(controller *gomock.Controller) Devices { +// mockDevices := mock_devices.NewMockDevices(controller) +// mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(1), nil) +// mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(1), nil) +// return mockDevices +// }, +// getFs: func(controller *gomock.Controller) afero.Fs { +// fs := afero.NewMemMapFs() +// fs.Create(DevPrefix + deviceName) +// fs.Create(multipathDeviceName) +// return fs +// }, +// assertError: assert.NoError, +// assertValid: assert.True, +// expectedDeviceSize: 0, +// }, +// } +// +// for name, params := range tests { +// t.Run(name, func(t *testing.T) { +// ctrl := gomock.NewController(t) +// var fs afero.Fs +// if params.getFs != nil { +// fs = params.getFs(ctrl) +// } +// client := NewDetailed(execCmd.NewCommand(), fs) +// deviceSize, valid, err := client.VerifyMultipathDeviceSize(context.TODO(), multipathDeviceName, deviceName) +// if params.assertError != nil { +// params.assertError(t, err) +// } +// if params.assertValid != nil { +// params.assertValid(t, valid) +// } +// assert.Equal(t, params.expectedDeviceSize, deviceSize) +// }) +// } +//} + +func TestWaitForDevicesRemoval(t *testing.T) { + errMsg := "timed out waiting for devices to be removed" + tests := map[string]struct { + name string + devicePathPrefix string + deviceNames []string + getOsFs func() (afero.Fs, error) + maxWaitTime time.Duration + expectedError error + }{ + "Devices removed successfully": { + devicePathPrefix: "/dev", + deviceNames: []string{"sda", "sdb"}, + getOsFs: func() (afero.Fs, error) { + return afero.NewMemMapFs(), nil + }, + maxWaitTime: 1 * time.Second, + expectedError: nil, + }, + "Timeout waiting for devices to be removed": { + devicePathPrefix: "/dev", + deviceNames: []string{"sda", "sdb"}, + getOsFs: func() (afero.Fs, error) { + osFs := afero.NewMemMapFs() + _, err := osFs.Create("/dev/sda") + if err != nil { + return nil, err + } + _, err = osFs.Create("/dev/sdb") + if err != nil { + return nil, err + } + return osFs, nil + }, + maxWaitTime: 1 * time.Second, + expectedError: tridentError.TimeoutError(errMsg), + }, + "Timeout waiting for last device to be removed": { + devicePathPrefix: "/dev", + deviceNames: []string{"sda", "sdb"}, + getOsFs: func() (afero.Fs, error) { + osFs := afero.NewMemMapFs() + _, err := osFs.Create("/dev/sdb") + if err != nil { + return nil, err + } + return osFs, nil + }, + maxWaitTime: 1 * time.Second, + expectedError: tridentError.TimeoutError(errMsg), + }, + "Timeout waiting for first device to be removed": { + devicePathPrefix: "/dev", + deviceNames: []string{"sda", "sdb"}, + getOsFs: func() (afero.Fs, error) { + osFs := afero.NewMemMapFs() + _, err := osFs.Create("/dev/sda") + if err != nil { + return nil, err + } + return osFs, nil + }, + maxWaitTime: 1 * time.Second, + expectedError: tridentError.TimeoutError(errMsg), + }, + } + + for name, params := range tests { + t.Run(name, func(t *testing.T) { + fs, err := params.getOsFs() + assert.NoError(t, err) + devices := NewDetailed(nil, fs) + err = devices.WaitForDevicesRemoval(context.Background(), params.devicePathPrefix, params.deviceNames, + params.maxWaitTime) + if params.expectedError != nil { + assert.EqualError(t, err, params.expectedError.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/utils/devices/devices_windows.go b/utils/devices/devices_windows.go new file mode 100644 index 000000000..54a759358 --- /dev/null +++ b/utils/devices/devices_windows.go @@ -0,0 +1,70 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +// NOTE: This file should only contain functions for handling devices for windows flavor + +package devices + +import ( + "golang.org/x/net/context" + + . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/errors" +) + +// FlushOneDevice unused stub function +func (c *Client) FlushOneDevice(ctx context.Context, devicePath string) error { + Logc(ctx).Debug(">>>> devices_windows.FlushOneDevice") + defer Logc(ctx).Debug("<<<< devices_windows.FlushOneDevice") + return errors.UnsupportedError("FlushOneDevice is not supported for windows") +} + +func (c *Client) ListAllDevices(ctx context.Context) { + Logc(ctx).Debug(">>>> devices_windows.ListAllDevices") + defer Logc(ctx).Debug("<<<< devices_windows.ListAllDevices") + Logc(ctx).Debug("ListAllDevices is not supported for windows") +} + +func (c *Client) WaitForDevice(ctx context.Context, device string) error { + Logc(ctx).Debug(">>>> devices_windows.WaitForDevice") + defer Logc(ctx).Debug("<<<< devices_windows.WaitForDevice") + return errors.UnsupportedError("ListAllDevices is not supported for windows") +} + +func (c *Client) VerifyMultipathDeviceSize(ctx context.Context, multipathDevice, device string) (int64, bool, error) { + Logc(ctx).Debug(">>>> devices_windows.VerifyMultipathDeviceSize") + defer Logc(ctx).Debug("<<<< devices_windows.VerifyMultipathDeviceSize") + return 0, false, errors.UnsupportedError("VerifyMultipathDeviceSize is not supported for windows") +} + +// GetDiskSize unused stub function +func (c *Client) GetDiskSize(ctx context.Context, _ string) (int64, error) { + Logc(ctx).Debug(">>>> devices_windows.GetDiskSize") + defer Logc(ctx).Debug("<<<< devices_windows.GetDiskSize") + return 0, errors.UnsupportedError("GetDiskSize is not supported for windows") +} + +func (c *Client) EnsureLUKSDeviceClosed(ctx context.Context, luksDevicePath string) error { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + + Logc(ctx).Debug(">>>> devices_windows.EnsureLUKSDeviceClosed") + defer Logc(ctx).Debug("<<<< devices_windows.EnsureLUKSDeviceClosed") + return errors.UnsupportedError("EnsureLUKSDeviceClosed is not supported for windows") +} + +func (c *Client) GetDeviceFSType(ctx context.Context, device string) (string, error) { + Logc(ctx).Debug(">>>> devices_windows.GetDeviceFSType") + defer Logc(ctx).Debug("<<<< devices_windows.GetDeviceFSType") + return "", errors.UnsupportedError("GetDeviceFSTypeis not supported for windows") +} + +func (c *Client) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { + Logc(ctx).Debug(">>>> devices_windows.EnsureLUKSDeviceClosedWithMaxWaitLimit") + defer Logc(ctx).Debug("<<<< devices_windows.EnsureLUKSDeviceClosedWithMaxWaitLimit") + return errors.UnsupportedError("EnsureLUKSDeviceClosedWithMaxWaitLimit is not supported for windows") +} + +func (c *Client) CloseLUKSDevice(ctx context.Context, devicePath string) error { + Logc(ctx).Debug(">>>> devices_windows.CloseLUKSDevice") + defer Logc(ctx).Debug("<<<< devices_windows.CloseLUKSDevice") + return errors.UnsupportedError("CloseLUKSDevice is not supported for windows") +} diff --git a/utils/devices_windows_test.go b/utils/devices/devices_windows_test.go similarity index 60% rename from utils/devices_windows_test.go rename to utils/devices/devices_windows_test.go index 8436b4e64..64bd50cf9 100644 --- a/utils/devices_windows_test.go +++ b/utils/devices/devices_windows_test.go @@ -1,28 +1,32 @@ // Copyright 2022 NetApp, Inc. All Rights Reserved. -package utils +package devices import ( "context" "testing" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/exec" ) func TestFlushOneDevice(t *testing.T) { ctx := context.Background() - result := flushOneDevice(ctx, "/test/path") + devices := NewDetailed(exec.NewCommand(), afero.NewMemMapFs()) + result := devices.FlushOneDevice(ctx, "/test/path") assert.Error(t, result, "no error") assert.True(t, errors.IsUnsupportedError(result), "not UnsupportedError") } -func TestGetISCSIDiskSize(t *testing.T) { +func TestGetDiskSize(t *testing.T) { ctx := context.Background() - result, err := getISCSIDiskSize(ctx, "/test/path") + devices := NewDetailed(exec.NewCommand(), afero.NewMemMapFs()) + result, err := devices.GetDiskSize(ctx, "/test/path") assert.Equal(t, result, int64(0), "received disk size") assert.Error(t, err, "no error") assert.True(t, errors.IsUnsupportedError(err), "not UnsupportedError") diff --git a/utils/devices/luks/luks.go b/utils/devices/luks/luks.go new file mode 100644 index 000000000..77c85f2ed --- /dev/null +++ b/utils/devices/luks/luks.go @@ -0,0 +1,181 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +package luks + +//go:generate mockgen -destination=../../../mocks/mock_utils/mock_devices/mock_luks/mock_luks.go -package mock_luks github.com/netapp/trident/utils/devices/luks Device + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/afero" + + . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/devices" + "github.com/netapp/trident/utils/errors" + execCmd "github.com/netapp/trident/utils/exec" +) + +const ( + luksDevicePrefix = "luks-" + // LUKS2 requires ~16MiB for overhead. Default to 18MiB just in case. + LUKSMetadataSize = 18874368 +) + +type Device interface { + EnsureLUKSDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, error) + MappedDevicePath() string + MappedDeviceName() string + RawDevicePath() string + EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (bool, error) + CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) + RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error +} + +type LUKSDevice struct { + rawDevicePath string + mappedDeviceName string + command execCmd.Command + devices devices.Devices + osFs afero.Fs +} + +func NewLUKSDevice(rawDevicePath, volumeId string, command execCmd.Command) *LUKSDevice { + luksDeviceName := luksDevicePrefix + volumeId + devices := devices.New() + osFs := afero.NewOsFs() + return NewDetailed(rawDevicePath, luksDeviceName, command, devices, osFs) +} + +func NewDetailed(rawDevicePath, mappedDeviceName string, command execCmd.Command, devices devices.Devices, + osFs afero.Fs, +) *LUKSDevice { + return &LUKSDevice{ + rawDevicePath: rawDevicePath, + mappedDeviceName: mappedDeviceName, + command: command, + devices: devices, + osFs: osFs, + } +} + +func NewLUKSDeviceFromMappingPath( + ctx context.Context, command execCmd.Command, mappingPath, volumeId string, +) (*LUKSDevice, error) { + rawDevicePath, err := GetUnderlyingDevicePathForLUKSDevice(ctx, command, mappingPath) + if err != nil { + return nil, fmt.Errorf("could not determine underlying device for LUKS mapping; %v", err) + } + return NewLUKSDevice(rawDevicePath, volumeId, command), nil +} + +// EnsureLUKSDeviceMappedOnHost ensures the specified device is LUKS formatted, opened, and has the current passphrase. +func (d *LUKSDevice) EnsureLUKSDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, error) { + // Try to Open with current luks passphrase + luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase := GetLUKSPassphrasesFromSecretMap(secrets) + if luksPassphrase == "" { + return false, fmt.Errorf("LUKS passphrase cannot be empty") + } + if luksPassphraseName == "" { + return false, fmt.Errorf("LUKS passphrase name cannot be empty") + } + + Logc(ctx).WithFields(LogFields{ + "volume": name, + "luks-passphrase-name": luksPassphraseName, + }).Info("Opening encrypted volume.") + luksFormatted, err := d.EnsureFormattedAndOpen(ctx, luksPassphrase) + if err == nil { + return luksFormatted, nil + } + + // If we failed to open, try previous passphrase + if previousLUKSPassphrase == "" { + // Return original error if there is no previous passphrase to use + return luksFormatted, fmt.Errorf("could not open LUKS device; %v", err) + } + if luksPassphrase == previousLUKSPassphrase { + return luksFormatted, fmt.Errorf("could not open LUKS device, previous passphrase matches current") + } + if previousLUKSPassphraseName == "" { + return luksFormatted, fmt.Errorf("could not open LUKS device, no previous passphrase name provided") + } + Logc(ctx).WithFields(LogFields{ + "volume": name, + "luks-passphrase-name": previousLUKSPassphraseName, + }).Info("Opening encrypted volume.") + luksFormatted, err = d.EnsureFormattedAndOpen(ctx, previousLUKSPassphrase) + if err != nil { + return luksFormatted, fmt.Errorf("could not open LUKS device; %v", err) + } + + return luksFormatted, nil +} + +// MappedDevicePath returns the location of the LUKS device when opened. +func (d *LUKSDevice) MappedDevicePath() string { + return devices.DevMapperRoot + d.mappedDeviceName +} + +// MappedDeviceName returns the name of the LUKS device when opened. +func (d *LUKSDevice) MappedDeviceName() string { + return d.mappedDeviceName +} + +// RawDevicePath returns the original device location, such as the iscsi device or multipath device. Ex: /dev/sdb +func (d *LUKSDevice) RawDevicePath() string { + return d.rawDevicePath +} + +// EnsureFormattedAndOpen ensures the specified device is LUKS formatted and opened. +func (d *LUKSDevice) EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (formatted bool, err error) { + return d.ensureLUKSDevice(ctx, luksPassphrase) +} + +func (d *LUKSDevice) ensureLUKSDevice(ctx context.Context, luksPassphrase string) (bool, error) { + // First check if LUKS device is already opened. This is OK to check even if the device isn't LUKS formatted. + if isOpen, err := d.IsOpen(ctx); err != nil { + // If the LUKS device isn't found, it means that we need to check if the device is LUKS formatted. + // If it isn't, then we should format it and attempt to open it. + // If any other error occurs, bail out. + if !errors.IsNotFoundError(err) { + Logc(ctx).WithError(err).Error("Could not check if device is an open LUKS device.") + return false, err + } + } else if isOpen { + Logc(ctx).Debug("Device is LUKS formatted and open.") + return true, nil + } + + if err := d.lUKSFormat(ctx, luksPassphrase); err != nil { + Logc(ctx).WithError(err).Error("Could not LUKS format device.") + return false, fmt.Errorf("could not LUKS format device; %w", err) + } + + // At this point, we should be able to open the device. + if err := d.Open(ctx, luksPassphrase); err != nil { + // At this point, we couldn't open the LUKS device, but we do know + // the device is LUKS formatted because LUKSFormat didn't fail. + Logc(ctx).WithError(err).Error("Could not open LUKS formatted device.") + return true, fmt.Errorf("could not open LUKS device; %v", err) + } + + Logc(ctx).Debug("Device is LUKS formatted and open.") + return true, nil +} + +func GetLUKSPassphrasesFromSecretMap(secrets map[string]string) (string, string, string, string) { + var luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase string + luksPassphrase = secrets["luks-passphrase"] + luksPassphraseName = secrets["luks-passphrase-name"] + previousLUKSPassphrase = secrets["previous-luks-passphrase"] + previousLUKSPassphraseName = secrets["previous-luks-passphrase-name"] + + return luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase +} + +// IsLegacyLUKSDevicePath returns true if the device path points to mapped LUKS device instead of mpath device. +func IsLegacyLUKSDevicePath(devicePath string) bool { + return strings.Contains(devicePath, "luks") +} diff --git a/utils/devices/luks/luks_darwin.go b/utils/devices/luks/luks_darwin.go new file mode 100644 index 000000000..4567ad7a4 --- /dev/null +++ b/utils/devices/luks/luks_darwin.go @@ -0,0 +1,65 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +package luks + +import ( + "golang.org/x/net/context" + + . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/exec" +) + +func (d *LUKSDevice) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) { + Logc(ctx).Debug(">>>> devices_darwin.CheckPassphrase") + defer Logc(ctx).Debug("<<<< devices_darwin.CheckPassphrase") + return false, errors.UnsupportedError("CheckPassphrase is not supported for darwin") +} + +func (d *LUKSDevice) RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error { + Logc(ctx).Debug(">>>> devices_darwin.RotatePassphrase") + defer Logc(ctx).Debug("<<<< devices_darwin.RotatePassphrase") + return errors.UnsupportedError("RotatePassphrase is not supported for darwin") +} + +// IsLUKSFormatted returns whether LUKS headers have been placed on the device +func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { + Logc(ctx).Debug(">>>> devices_darwin.IsLUKSFormatted") + defer Logc(ctx).Debug("<<<< devices_darwin.IsLUKSFormatted") + return false, errors.UnsupportedError("IsLUKSFormatted is not supported for darwin") +} + +// IsOpen returns whether the device is an active luks device on the host +func (d *LUKSDevice) IsOpen(ctx context.Context) (bool, error) { + Logc(ctx).Debug(">>>> devices_darwin.IsOpen") + defer Logc(ctx).Debug("<<<< devices_darwin.IsOpen") + return false, errors.UnsupportedError("IsOpen is not supported for darwin") +} + +// lUKSFormat attempts to set up LUKS headers on a device with the specified passphrase, but bails if the +// underlying device already has a format present that is not LUKS. +func (d *LUKSDevice) lUKSFormat(ctx context.Context, _ string) error { + Logc(ctx).Debug(">>>> devices_darwin.LUKSFormat") + defer Logc(ctx).Debug("<<<< devices_darwin.LUKSFormat") + return errors.UnsupportedError("LUKSFormat is not supported for darwin") +} + +// Open makes the device accessible on the host +func (d *LUKSDevice) Open(ctx context.Context, luksPassphrase string) error { + Logc(ctx).Debug(">>>> devices_darwin.Open") + defer Logc(ctx).Debug("<<<< devices_darwin.Open") + return errors.UnsupportedError("Open is not supported for darwin") +} + +func GetUnderlyingDevicePathForLUKSDevice(ctx context.Context, command exec.Command, luksDevicePath string) (string, error) { + Logc(ctx).Debug(">>>> devices_darwin.GetUnderlyingDevicePathForLUKSDevice") + defer Logc(ctx).Debug("<<<< devices_darwin.GetUnderlyingDevicePathForLUKSDevice") + return "", errors.UnsupportedError("GetUnderlyingDevicePathForLUKSDevice is not supported for darwin") +} + +// Resize performs a luksResize on the LUKS device +func (d *LUKSDevice) Resize(ctx context.Context, luksPassphrase string) error { + Logc(ctx).Debug(">>>> devices_darwin.Resize") + defer Logc(ctx).Debug("<<<< devices_darwin.Resize") + return errors.UnsupportedError("Resize is not supported for darwin") +} diff --git a/utils/devices_linux.go b/utils/devices/luks/luks_linux.go similarity index 57% rename from utils/devices_linux.go rename to utils/devices/luks/luks_linux.go index a1a9f5fb5..9473557ce 100644 --- a/utils/devices_linux.go +++ b/utils/devices/luks/luks_linux.go @@ -1,17 +1,12 @@ // Copyright 2024 NetApp, Inc. All Rights Reserved. -// NOTE: This file should only contain functions for handling devices for linux flavor - -package utils +package luks import ( "fmt" - "os" "os/exec" "strings" - "syscall" "time" - "unsafe" log "github.com/sirupsen/logrus" "golang.org/x/net/context" @@ -20,12 +15,11 @@ import ( "github.com/netapp/trident/internal/fiji" . "github.com/netapp/trident/logging" "github.com/netapp/trident/utils/errors" + execCmd "github.com/netapp/trident/utils/exec" ) const ( - luksCommandTimeout = time.Second * 30 - luksCypherMode = "aes-xts-plain64" - luksType = "luks2" + luksCommandTimeout time.Duration = time.Second * 30 // Return code for "no permission (bad passphrase)" from cryptsetup command luksCryptsetupBadPassphraseReturnCode = 2 @@ -40,140 +34,52 @@ const ( ) var ( - afterLuksClose = fiji.Register("afterLuksClose", "devices_linux") - beforeLuksClose = fiji.Register("beforeLuksClose", "devices_linux") beforeLuksCheck = fiji.Register("beforeLuksCheck", "devices_linux") - beforeBlockDeviceFlushBuffer = fiji.Register("beforeBlockDeviceFlushBuffer", "devices_linux") beforeCryptSetupFormat = fiji.Register("beforeCryptSetupFormat", "node_server") duringOpenBeforeCryptSetupOpen = fiji.Register("duringOpenBeforeCryptSetupOpen", "devices_linux") duringRotatePassphraseBeforeLuksKeyChange = fiji.Register("duringRotatePassphraseBeforeLuksKeyChange", "devices_linux") ) -// flushOneDevice flushes any outstanding I/O to a disk -func flushOneDevice(ctx context.Context, devicePath string) error { - fields := LogFields{"rawDevicePath": devicePath} - Logc(ctx).WithFields(fields).Debug(">>>> devices_linux.flushOneDevice") - defer Logc(ctx).WithFields(fields).Debug("<<<< devices_linux.flushOneDevice") - - if err := beforeBlockDeviceFlushBuffer.Inject(); err != nil { - return err - } - - out, err := command.ExecuteWithTimeout( - ctx, "blockdev", deviceOperationsTimeout, true, "--flushbufs", devicePath, +// GetUnderlyingDevicePathForLUKSDevice returns the device mapped to the LUKS device +// uses cryptsetup status and parses the output +func GetUnderlyingDevicePathForLUKSDevice(ctx context.Context, command execCmd.Command, luksDevicePath string) (string, + error, +) { + out, err := command.ExecuteWithTimeoutAndInput(ctx, "cryptsetup", luksCommandTimeout, true, + "", "status", luksDevicePath, ) - if err != nil { - Logc(ctx).WithFields( - LogFields{ - "error": err, - "output": string(out), - "device": devicePath, - }).Debug("blockdev --flushbufs failed.") - return fmt.Errorf("flush device failed for %s : %s", devicePath, err) - } - - return nil -} - -// getISCSIDiskSize queries the current block size in bytes -func getISCSIDiskSize(ctx context.Context, devicePath string) (int64, error) { - fields := LogFields{"rawDevicePath": devicePath} - Logc(ctx).WithFields(fields).Debug(">>>> devices_linux.getISCSIDiskSize") - defer Logc(ctx).WithFields(fields).Debug("<<<< devices_linux.getISCSIDiskSize") - disk, err := os.Open(devicePath) - if err != nil { - Logc(ctx).Error("Failed to open disk.") - return 0, fmt.Errorf("failed to open disk %s: %s", devicePath, err) - } - defer disk.Close() - - var size int64 - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, disk.Fd(), unix.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) - if errno != 0 { - err := os.NewSyscallError("ioctl", errno) - Logc(ctx).Error("BLKGETSIZE64 ioctl failed") - return 0, fmt.Errorf("BLKGETSIZE64 ioctl failed %s: %s", devicePath, err) - } - - return size, nil -} - -// getISCSIDiskSize queries the current block size in bytes -func getFCPDiskSize(ctx context.Context, devicePath string) (int64, error) { - fields := LogFields{"rawDevicePath": devicePath} - Logc(ctx).WithFields(fields).Debug(">>>> devices_linux.getFCPDiskSize") - defer Logc(ctx).WithFields(fields).Debug("<<<< devices_linux.getFCPDiskSize") - - disk, err := os.Open(devicePath) + output := string(out) if err != nil { - Logc(ctx).Error("Failed to open disk.") - return 0, fmt.Errorf("failed to open disk %s: %s", devicePath, err) - } - defer disk.Close() - - var size int64 - // TODO (vhs): Check if syscall can be avoided. - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, disk.Fd(), unix.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) - if errno != 0 { - err := os.NewSyscallError("ioctl", errno) - Logc(ctx).Error("BLKGETSIZE64 ioctl failed: ", err) - return 0, fmt.Errorf("BLKGETSIZE64 ioctl failed %s: %s", devicePath, err) - } - - return size, nil -} - -// IsLUKSFormatted returns whether LUKS headers have been placed on the device -func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - if d.RawDevicePath() == "" { - return false, fmt.Errorf("no device path for LUKS device") - } - device := d.RawDevicePath() - - Logc(ctx).WithField("device", device).Debug("Checking if device is a LUKS device.") - - if err := beforeLuksCheck.Inject(); err != nil { - return false, err + return "", err + } else if !strings.Contains(output, "device:") { + return "", fmt.Errorf("cryptsetup status command output does not contain a device entry") } - output, err := command.ExecuteWithTimeoutAndInput( - ctx, "cryptsetup", luksCommandTimeout, true, "", "isLuks", device, - ) - if err != nil { - fields := LogFields{"device": device, "output": string(output)} - - // If the error isn't an exit error, then some other issue happened. - exitError, ok := err.(*exec.ExitError) - if !ok { - Logc(ctx).WithFields(fields).WithError(err).Debug("Failed to check if device is LUKS.") - return false, fmt.Errorf("could not check if device is a LUKS device: %v", err) + var devicePath string + for _, line := range strings.Split(output, "\n") { + if !strings.Contains(line, "device:") { + continue } - // If any status code aside from "0" and "1" occur, fail. Checking for "0" is an extra safety precaution. - status := exitError.ExitCode() - if status != cryptsetupIsLuksDeviceIsNotLuksStatusCode && status != cryptsetupIsLuksDeviceIsLuksStatusCode { - Logc(ctx).WithFields(fields).WithError(exitError).Debug("Failed to check if device is a LUKS device.") - return false, fmt.Errorf("unrecognized exit error from cryptsetup isLuks; %w", exitError) + // If the "device: ..." line from cryptsetup must be able to be broken up into at least 2 fields. + // line: " device: /dev/mapper/3600a09807770457a795d526950374c76" + // pieces: ["device: "," /dev/mapper/3600a09807770457a795d526950374c76"] + pieces := strings.Fields(line) + if len(pieces) != 2 { + break } - // At this point we know the device is not LUKS formatted. - Logc(ctx).WithFields(fields).WithError(exitError).Info("Device is not a LUKS device.") - return false, nil + devicePath = strings.TrimSpace(pieces[1]) + break } - Logc(ctx).WithField("device", device).Debug("Device is a LUKS device.") - return true, nil -} - -// IsOpen returns whether the device is an active luks device on the host -func (d *LUKSDevice) IsOpen(ctx context.Context) (bool, error) { - GenerateRequestContextForLayer(ctx, LogLayerUtils) + if devicePath == "" { + return "", fmt.Errorf("failed to get underlying device path from device: %s", luksDevicePath) + } - return IsLUKSDeviceOpen(ctx, d.MappedDevicePath()) + return devicePath, nil } // luksFormat sets up LUKS headers on the device with the specified passphrase, this destroys data on the device @@ -191,7 +97,7 @@ func (d *LUKSDevice) luksFormat(ctx context.Context, luksPassphrase string) erro return err } - if output, err := command.ExecuteWithTimeoutAndInput( + if output, err := d.command.ExecuteWithTimeoutAndInput( ctx, "cryptsetup", luksCommandTimeout, true, luksPassphrase, "luksFormat", device, "--type", "luks2", "-c", "aes-xts-plain64", ); err != nil { @@ -205,9 +111,9 @@ func (d *LUKSDevice) luksFormat(ctx context.Context, luksPassphrase string) erro return nil } -// LUKSFormat attempts to set up LUKS headers on a device with the specified passphrase, but bails out if the +// lUKSFormat attempts to set up LUKS headers on a device with the specified passphrase, but bails out if the // underlying device already has a format present. -func (d *LUKSDevice) LUKSFormat(ctx context.Context, luksPassphrase string) error { +func (d *LUKSDevice) lUKSFormat(ctx context.Context, luksPassphrase string) error { fields := LogFields{"device": d.RawDevicePath()} Logc(ctx).WithFields(fields).Debug("Attempting to LUKS format device.") @@ -220,7 +126,7 @@ func (d *LUKSDevice) LUKSFormat(ctx context.Context, luksPassphrase string) erro } // Ensure the device is empty before attempting LUKS format. - if unformatted, err := isDeviceUnformatted(ctx, d.RawDevicePath()); err != nil { + if unformatted, err := d.devices.IsDeviceUnformatted(ctx, d.RawDevicePath()); err != nil { return fmt.Errorf("failed to check if device is unformatted; %w", err) } else if !unformatted { return fmt.Errorf("cannot LUKS format device; device is not empty") @@ -242,6 +148,57 @@ func (d *LUKSDevice) LUKSFormat(ctx context.Context, luksPassphrase string) erro return nil } +// IsLUKSFormatted returns whether LUKS headers have been placed on the device +func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + + if d.RawDevicePath() == "" { + return false, fmt.Errorf("no device path for LUKS device") + } + device := d.RawDevicePath() + + Logc(ctx).WithField("device", device).Debug("Checking if device is a LUKS device.") + + if err := beforeLuksCheck.Inject(); err != nil { + return false, err + } + + output, err := d.command.ExecuteWithTimeoutAndInput( + ctx, "cryptsetup", luksCommandTimeout, true, "", "isLuks", device, + ) + if err != nil { + fields := LogFields{"device": device, "output": string(output)} + + // If the error isn't an exit error, then some other issue happened. + exitError, ok := err.(execCmd.ExitError) + if !ok { + Logc(ctx).WithFields(fields).WithError(err).Debug("Failed to check if device is LUKS.") + return false, fmt.Errorf("could not check if device is a LUKS device: %v", err) + } + + // If any status code aside from "0" and "1" occur, fail. Checking for "0" is an extra safety precaution. + status := exitError.ExitCode() + if status != cryptsetupIsLuksDeviceIsNotLuksStatusCode && status != cryptsetupIsLuksDeviceIsLuksStatusCode { + Logc(ctx).WithFields(fields).WithError(exitError).Debug("Failed to check if device is a LUKS device.") + return false, fmt.Errorf("unrecognized exit error from cryptsetup isLuks; %w", exitError) + } + + // At this point we know the device is not LUKS formatted. + Logc(ctx).WithFields(fields).WithError(exitError).Info("Device is not a LUKS device.") + return false, nil + } + + Logc(ctx).WithField("device", device).Debug("Device is a LUKS device.") + return true, nil +} + +// IsOpen returns whether the device is an active luks device on the host +func (d *LUKSDevice) IsOpen(ctx context.Context) (bool, error) { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + + return d.IsLUKSDeviceOpen(ctx, d.MappedDevicePath()) +} + // Open makes the device accessible on the host func (d *LUKSDevice) Open(ctx context.Context, luksPassphrase string) error { GenerateRequestContextForLayer(ctx, LogLayerUtils) @@ -259,7 +216,7 @@ func (d *LUKSDevice) Open(ctx context.Context, luksPassphrase string) error { return err } - output, err := command.ExecuteWithTimeoutAndInput( + output, err := d.command.ExecuteWithTimeoutAndInput( ctx, "cryptsetup", luksCommandTimeout, true, luksPassphrase, "open", device, luksDeviceName, "--type", "luks2", ) @@ -283,48 +240,18 @@ func (d *LUKSDevice) Open(ctx context.Context, luksPassphrase string) error { return nil } -// Close performs a luksClose on the LUKS device -func (d *LUKSDevice) Close(ctx context.Context) error { - // Need to Close the LUKS device - return closeLUKSDevice(ctx, d.MappedDevicePath()) -} - -// closeLUKSDevice performs a luksClose on the device at the specified path (example: "/dev/mapper/"). -func closeLUKSDevice(ctx context.Context, devicePath string) error { - if err := beforeLuksClose.Inject(); err != nil { - return err - } - - output, err := command.ExecuteWithTimeoutAndInput( - ctx, "cryptsetup", luksCommandTimeout, true, "", "luksClose", devicePath, - ) - - if err := afterLuksClose.Inject(); err != nil { - return err - } - - if err != nil { - fields := LogFields{"luksDevicePath": devicePath, "output": string(output)} - Logc(ctx).WithFields(fields).WithError(err).Debug("Failed to close LUKS device") - return fmt.Errorf("failed to close LUKS device %s; %w", devicePath, err) - } - - Logc(ctx).WithField("luksDevicePath", devicePath).Debug("Closed LUKS device.") - return nil -} - // IsLUKSDeviceOpen returns whether the device at the specified path is an open LUKS device. -func IsLUKSDeviceOpen(ctx context.Context, devicePath string) (bool, error) { +func (d *LUKSDevice) IsLUKSDeviceOpen(ctx context.Context, devicePath string) (bool, error) { GenerateRequestContextForLayer(ctx, LogLayerUtils) Logc(ctx).WithField("devicePath", devicePath).Debug("Checking if device is an open LUKS device.") - output, err := command.ExecuteWithTimeoutAndInput( + output, err := d.command.ExecuteWithTimeoutAndInput( ctx, "cryptsetup", luksCommandTimeout, true, "", "status", devicePath, ) if err != nil { fields := LogFields{"devicePath": devicePath, "output": string(output)} - exitError, ok := err.(*exec.ExitError) + exitError, ok := err.(execCmd.ExitError) if !ok { return false, err } @@ -349,112 +276,6 @@ func IsLUKSDeviceOpen(ctx context.Context, devicePath string) (bool, error) { return true, nil } -// EnsureLUKSDeviceClosed ensures there is no open LUKS device at the specified path (example: "/dev/mapper/"). -func EnsureLUKSDeviceClosed(ctx context.Context, devicePath string) error { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - _, err := osFs.Stat(devicePath) - if err == nil { - return closeLUKSDevice(ctx, devicePath) - } else if !os.IsNotExist(err) { - Logc(ctx).WithFields(LogFields{ - "device": devicePath, - "error": err.Error(), - }).Debug("Failed to stat device.") - return fmt.Errorf("could not stat device: %s; %v", devicePath, err) - } - Logc(ctx).WithFields(LogFields{ - "device": devicePath, - }).Debug("LUKS device not found.") - - return nil -} - -func EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { - if err := EnsureLUKSDeviceClosed(ctx, luksDevicePath); err != nil { - LuksCloseDurations.InitStartTime(luksDevicePath) - elapsed, durationErr := LuksCloseDurations.GetCurrentDuration(luksDevicePath) - if durationErr != nil { - return durationErr - } - if elapsed > luksCloseMaxWaitDuration { - Logc(ctx).WithFields( - LogFields{ - "device": luksDevicePath, - "elapsed": elapsed, - "maxWait": luksDevicePath, - }).Debug("LUKS close max wait time expired, continuing with removal.") - return errors.MaxWaitExceededError(fmt.Sprintf("LUKS close wait time expired. Elapsed: %v", elapsed)) - } - return err - } - return nil -} - -// getDeviceFSType returns the filesystem for the supplied device. -func getDeviceFSType(ctx context.Context, device string) (string, error) { - Logc(ctx).WithField("device", device).Debug(">>>> devices.getDeviceFSType") - defer Logc(ctx).Debug("<<<< devices.getDeviceFSType") - - // blkid return status=2 both in case of an unformatted filesystem as well as for the case when it is - // unable to get the filesystem (e.g. IO error), therefore ensure device is available before calling blkid - if err := waitForDevice(ctx, device); err != nil { - return "", fmt.Errorf("could not find device before checking for the filesystem %v; %s.", device, err) - } - - out, err := command.ExecuteWithTimeout(ctx, "blkid", 5*time.Second, true, device) - if err != nil { - if errors.IsTimeoutError(err) { - listAllISCSIDevices(ctx) - return "", err - } else if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 2 { - // EITHER: Disk device is unformatted. - // OR: For 'blkid', if the specified token (TYPE/PTTYPE, etc) was - // not found, or no (specified) devices could be identified, an - // exit code of 2 is returned. - - Logc(ctx).WithField("device", device).Infof("Could not get FSType for device; err: %v.", err) - return "", nil - } - - Logc(ctx).WithField("device", device).Errorf("Could not determine FSType for device; err: %v.", err) - return "", err - } - - var fsType string - - if strings.Contains(string(out), "TYPE=") { - for _, v := range strings.Split(string(out), " ") { - if strings.Contains(v, "TYPE=") { - fsType = strings.Split(v, "=")[1] - fsType = strings.Replace(fsType, "\"", "", -1) - fsType = strings.TrimSpace(fsType) - } - } - } - - if fsType == "" { - Logc(ctx).WithField("out", string(out)).Errorf("Unable to identify fsType.") - - // Read the device to see if it is in fact formatted - if unformatted, err := isDeviceUnformatted(ctx, device); err != nil { - Logc(ctx).WithFields(LogFields{ - "device": device, - "err": err, - }).Debugf("Unable to identify if the device is not unformatted.") - } else if !unformatted { - Logc(ctx).WithField("device", device).Debugf("Device is not unformatted.") - return unknownFstype, nil - } else { - // If we are here blkid should have not retured exit status 0, we need to retry. - Logc(ctx).WithField("device", device).Errorf("Device is unformatted.") - } - - return "", fmt.Errorf("unable to identify fsType") - } - - return fsType, nil -} - // RotatePassphrase changes the passphrase in passphrase slot 1, in place, to the specified passphrase. func (d *LUKSDevice) RotatePassphrase( ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string, @@ -478,7 +299,7 @@ func (d *LUKSDevice) RotatePassphrase( // Write the old passphrase to an anonymous file in memory because we can't provide it on stdin tempFileName := fmt.Sprintf("luks_key_%s", volumeId) - fd, err := fsClient.GenerateAnonymousMemFile(tempFileName, previousLUKSPassphrase) + fd, err := generateAnonymousMemFile(tempFileName, previousLUKSPassphrase) if err != nil { Log().WithFields(LogFields{ "error": err, @@ -494,7 +315,7 @@ func (d *LUKSDevice) RotatePassphrase( return err } - output, err := command.ExecuteWithTimeoutAndInput( + output, err := d.command.ExecuteWithTimeoutAndInput( ctx, "cryptsetup", luksCommandTimeout, true, luksPassphrase, "luksChangeKey", "-d", oldKeyFilename, d.RawDevicePath(), ) @@ -513,9 +334,28 @@ func (d *LUKSDevice) RotatePassphrase( return nil } +// generateAnonymousMemFile uses linux syscall memfd_create to create an anonymous, temporary, in-memory file +// with the specified name and contents +func generateAnonymousMemFile(tempFileName, content string) (int, error) { + fd, err := unix.MemfdCreate(tempFileName, 0) + if err != nil { + return -1, fmt.Errorf("failed to create anonymous file; %v", err) + } + _, err = unix.Write(fd, []byte(content)) + if err != nil { + return fd, fmt.Errorf("failed to write anonymous file; %v", err) + } + // Rewind back to the beginning + _, err = unix.Seek(fd, 0, 0) + if err != nil { + return fd, fmt.Errorf("failed to rewind anonymous file; %v", err) + } + return fd, nil +} + // Resize performs a luksResize on the LUKS device func (d *LUKSDevice) Resize(ctx context.Context, luksPassphrase string) error { - output, err := command.ExecuteWithTimeoutAndInput(ctx, "cryptsetup", luksCommandTimeout, true, + output, err := d.command.ExecuteWithTimeoutAndInput(ctx, "cryptsetup", luksCommandTimeout, true, luksPassphrase, "resize", d.MappedDevicePath(), ) if nil != err { @@ -534,55 +374,16 @@ func (d *LUKSDevice) Resize(ctx context.Context, luksPassphrase string) error { return nil } -// GetUnderlyingDevicePathForLUKSDevice returns the device mapped to the LUKS device -// uses cryptsetup status and parses the output -func GetUnderlyingDevicePathForLUKSDevice(ctx context.Context, luksDevicePath string) (string, error) { - out, err := command.ExecuteWithTimeoutAndInput(ctx, "cryptsetup", luksCommandTimeout, true, - "", "status", luksDevicePath, - ) - - output := string(out) - if err != nil { - return "", err - } else if !strings.Contains(output, "device:") { - return "", fmt.Errorf("cryptsetup status command output does not contain a device entry") - } - - var devicePath string - for _, line := range strings.Split(output, "\n") { - if !strings.Contains(line, "device:") { - continue - } - - // If the "device: ..." line from cryptsetup must be able to be broken up into at least 2 fields. - // line: " device: /dev/mapper/3600a09807770457a795d526950374c76" - // pieces: ["device: "," /dev/mapper/3600a09807770457a795d526950374c76"] - pieces := strings.Fields(line) - if len(pieces) != 2 { - break - } - - devicePath = strings.TrimSpace(pieces[1]) - break - } - - if devicePath == "" { - return "", fmt.Errorf("failed to get underlying device path from device: %s", luksDevicePath) - } - - return devicePath, nil -} - // CheckPassphrase returns whether the specified passphrase string is the current LUKS passphrase for the device func (d *LUKSDevice) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) { device := d.RawDevicePath() luksDeviceName := d.MappedDeviceName() - output, err := command.ExecuteWithTimeoutAndInput( + output, err := d.command.ExecuteWithTimeoutAndInput( ctx, "cryptsetup", luksCommandTimeout, true, luksPassphrase, "open", device, luksDeviceName, "--type", "luks2", "--test-passphrase", ) if err != nil { - if exiterr, ok := err.(*exec.ExitError); ok && exiterr.ExitCode() == luksCryptsetupBadPassphraseReturnCode { + if exiterr, ok := err.(execCmd.ExitError); ok && exiterr.ExitCode() == luksCryptsetupBadPassphraseReturnCode { return false, nil } Logc(ctx).WithError(err).Errorf("Cryptsetup command failed, output: %s", output) diff --git a/utils/devices/luks/luks_linux_test.go b/utils/devices/luks/luks_linux_test.go new file mode 100644 index 000000000..8f541dc4c --- /dev/null +++ b/utils/devices/luks/luks_linux_test.go @@ -0,0 +1,751 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +//go:build linux + +package luks + +import ( + "context" + "fmt" + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + "golang.org/x/sys/unix" + + "github.com/netapp/trident/mocks/mock_utils/mock_devices" + "github.com/netapp/trident/mocks/mock_utils/mock_exec" + mockexec "github.com/netapp/trident/mocks/mock_utils/mock_exec" + "github.com/netapp/trident/utils/devices" + "github.com/netapp/trident/utils/errors" +) + +var luksError = fmt.Errorf("luks error") + +func mockCryptsetupIsLuks(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, "", "isLuks", gomock.Any(), + ) +} + +func mockCryptsetupLuksFormat(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, gomock.Any(), + "luksFormat", gomock.Any(), "--type", "luks2", "-c", "aes-xts-plain64", + ) +} + +func mockCryptsetupLuksStatus(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, "", "status", gomock.Any(), + ) +} + +func mockCryptsetupLuksOpen(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, gomock.Any(), + "open", gomock.Any(), gomock.Any(), "--type", "luks2", + ) +} + +func mockCryptsetupLuksClose(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, "", "luksClose", gomock.Any(), + ) +} + +func mockCryptsetupLuksStatusWithDevicePath(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, "", "status", gomock.Any(), + ) +} + +func mockCryptsetupLuksTestPassphrase(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, gomock.Any(), "open", gomock.Any(), + gomock.Any(), "--type", "luks2", "--test-passphrase", + ) +} + +func mockCryptsetupLuksChangeKey(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, gomock.Any(), + "luksChangeKey", "-d", gomock.Any(), gomock.Any(), + ) +} + +func mockCryptsetupLuksResize(mock *mockexec.MockCommand) *gomock.Call { + return mock.EXPECT().ExecuteWithTimeoutAndInput( + gomock.Any(), "cryptsetup", luksCommandTimeout, true, + gomock.Any(), "resize", gomock.Any(), + ) +} + +func TestLUKSDevice_IsLUKSFormatted(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + luksDevice := NewDetailed("/dev/sdb", "pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) + assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) + + // Setup mock calls and reassign any clients to their mock counterparts. + mockCryptsetupIsLuks(mockCommand).Return([]byte(""), nil) + + isFormatted, err := luksDevice.IsLUKSFormatted(context.Background()) + assert.NoError(t, err) + assert.True(t, isFormatted) +} + +func TestLUKSDevice_LUKSFormat(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + luksDevice := NewDetailed("/dev/sdb", "pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) + assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) + + // Setup mock calls and reassign any clients to their mock counterparts. + mockCryptsetupIsLuks(mockCommand).Return([]byte(""), nil) + + err := luksDevice.lUKSFormat(context.Background(), "passphrase") + assert.NoError(t, err) +} + +func TestLUKSDevice_LUKSFormat_FailsCheckingIfDeviceIsLUKS(t *testing.T) { + ctx := context.Background() + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + // Create fake luks device. + luksDevice := NewDetailed("/dev/sdb", "pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) + assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) + + // Mock any cryptsetup calls that may occur. + mockCryptsetupIsLuks(mockCommand).Return([]byte(""), luksError) + + err := luksDevice.lUKSFormat(ctx, "mysecretlukspassphrase") + assert.Error(t, err) +} + +func TestLUKSDevice_Open(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + luksDevice := NewDetailed("/dev/sdb", "pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) + assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) + + // Setup mock calls and reassign any clients to their mock counterparts. + mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), nil) + + err := luksDevice.Open(context.Background(), "passphrase") + assert.NoError(t, err) +} + +func TestLUKSDevice_IsOpen(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + luksDevice := NewDetailed("/dev/sdb", "pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) + assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) + + // Setup mock calls and reassign any clients to their mock counterparts. + mockCryptsetupLuksStatus(mockCommand).Return([]byte(""), nil) + + isOpen, err := luksDevice.IsOpen(context.Background()) + assert.NoError(t, err) + assert.True(t, isOpen) +} + +func TestLUKSDevice_MissingDevicePath(t *testing.T) { + luksDevice := LUKSDevice{mappedDeviceName: "pvc-test", rawDevicePath: ""} + isFormatted, err := luksDevice.IsLUKSFormatted(context.Background()) + assert.Error(t, err) + assert.False(t, isFormatted) + + err = luksDevice.luksFormat(context.Background(), "passphrase") + assert.Error(t, err) + + err = luksDevice.Open(context.Background(), "passphrase") + assert.Error(t, err) +} + +func TestLUKSDevice_ExecErrors(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + + // Setup mock calls and reassign any clients to their mock counterparts. + gomock.InOrder( + mockCryptsetupIsLuks(mockCommand).Return([]byte(""), luksError), + mockCryptsetupLuksFormat(mockCommand).Return([]byte(""), luksError), + mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), luksError), + mockCryptsetupLuksStatus(mockCommand).Return([]byte(""), luksError), + mockCryptsetupLuksClose(mockCommand).Return([]byte(""), luksError), + ) + + isFormatted, err := luksDevice.IsLUKSFormatted(context.Background()) + assert.Error(t, err) + assert.False(t, isFormatted) + + err = luksDevice.luksFormat(context.Background(), "passphrase") + assert.Error(t, err) + + err = luksDevice.Open(context.Background(), "passphrase") + assert.Error(t, err) + + isOpen, err := luksDevice.IsOpen(context.Background()) + assert.Error(t, err) + assert.False(t, isOpen) + + devicesClient := devices.NewDetailed(mockCommand, afero.NewMemMapFs()) + err = devicesClient.CloseLUKSDevice(context.Background(), luksDevice.MappedDevicePath()) + assert.Error(t, err) +} + +func TestEnsureLUKSDevice_FailsWithExecError(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + mockCryptsetupLuksStatus(mockCommand).Return([]byte(""), luksError) + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + + luksFormatted, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + assert.Error(t, err) + assert.Equal(t, false, luksFormatted) +} + +func TestEnsureLUKSDevice_IsOpen(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + // Setup mock calls and reassign any clients to their mock counterparts. + mockCryptsetupLuksStatus(mockCommand) + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + + luksFormatted, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + assert.NoError(t, err) + assert.True(t, luksFormatted) +} + +func TestEnsureLUKSDevice_LUKSFormatFails(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + // Setup mock calls and reassign any clients to their mock counterparts. + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, + mock_exec.NewMockExitError(cryptsetupStatusDeviceDoesNotExistStatusCode, "mock error")) + mockCryptsetupIsLuks(mockCommand).Return([]byte{}, + mock_exec.NewMockExitError(cryptsetupIsLuksDeviceIsNotLuksStatusCode, "mock error")) + mockCryptsetupLuksFormat(mockCommand).Return([]byte{}, luksError) + + mockDevices := mock_devices.NewMockDevices(mockCtrl) + mockDevices.EXPECT().IsDeviceUnformatted(gomock.Any(), gomock.Any()).Return(true, nil) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"pvc-test", mockCommand, mockDevices, afero.NewMemMapFs()) + + luksFormatted, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestLUKSDeviceOpen_FailsWithBadPassphrase(t *testing.T) { + ctx := context.Background() + fakeExitError := mock_exec.NewMockExitError(2, "mock error") // Exit code of 2 means the passphrase was bad. + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), fakeExitError) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + err := luksDevice.Open(ctx, "passphrase") + assert.Error(t, err) +} + +func TestLUKSDeviceOpen_MiscError(t *testing.T) { + ctx := context.Background() + fakeExitError := mock_exec.NewMockExitError(2, "mock error") // Exit code of 2 means the passphrase was bad. + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), fakeExitError) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + err := luksDevice.Open(ctx, "passphrase") + assert.Error(t, err) +} + +func TestLUKSDeviceOpen_ExecError(t *testing.T) { + ctx := context.Background() + fakeExitError := mock_exec.NewMockExitError(1, "mock error") // Exit code of 1 means the exec command failed. + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), fakeExitError) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) + err := luksDevice.Open(ctx, "passphrase") + assert.Error(t, err) + assert.ErrorContains(t, err, "could not open LUKS device; ") +} + +func TestRotateLUKSDevicePassphrase_NoError(t *testing.T) { + ctx := context.Background() + luksDeviceName := "luks-pvc-test" + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksChangeKey(mockCommand).Return([]byte(""), nil) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+luksDeviceName, mockCommand, devices.New(), afero.NewMemMapFs()) + err := luksDevice.RotatePassphrase(ctx, "pvc-test", "previous", "newpassphrase") + assert.NoError(t, err) +} + +func TestRotateLUKSDevicePassphrase_OldPassphraseEmpty(t *testing.T) { + ctx := context.Background() + luksDeviceName := "luks-pvc-test" + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+luksDeviceName, nil, nil, nil) + err := luksDevice.RotatePassphrase(ctx, "pvc-test", "", "newpassphrase") + assert.Error(t, err) +} + +func TestRotateLUKSDevicePassphrase_NewPassphraseEmpty(t *testing.T) { + ctx := context.Background() + luksDeviceName := "luks-pvc-test" + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+luksDeviceName, nil, nil, nil) + err := luksDevice.RotatePassphrase(ctx, "pvc-test", "oldpassphrase", "") + assert.Error(t, err) +} + +func TestRotateLUKSDevicePassphrase_CommandError(t *testing.T) { + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksChangeKey(mockCommand).Return([]byte(""), luksError) + + ctx := context.Background() + luksDeviceName := "luks-pvc-test" + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+luksDeviceName, mockCommand, nil, nil) + err := luksDevice.RotatePassphrase(ctx, "pvc-test", "previous", "newpassphrase") + assert.Error(t, err) +} + +func TestRotateLUKSDevicePassphrase_NoRawDevicePath(t *testing.T) { + ctx := context.Background() + luksDeviceName := "luks-pvc-test" + luksDevice := NewDetailed("", luksDevicePrefix+luksDeviceName, nil, nil, nil) + err := luksDevice.RotatePassphrase(ctx, "pvc-test", "previous", "newpassphrase") + assert.Error(t, err) +} + +func TestGetUnderlyingDevicePathForLUKSDevice_Succeeds(t *testing.T) { + ctx := context.Background() + execReturnValue := `/dev/mapper/luks-trident_pvc_0c6202cb_be41_46b7_bea9_7f2c5c2c4a41 is active and is in use. + type: LUKS2 + cipher: aes-xts-plain64 + keysize: 512 bits + key location: keyring + device: /dev/mapper/3600a09807770457a795d526950374c76 + sector size: 512 + offset: 32768 sectors + size: 2064384 sectors + mode: read/write` + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) + + devicePath, err := GetUnderlyingDevicePathForLUKSDevice(ctx, mockCommand, "") + assert.NoError(t, err) + assert.Equal(t, "/dev/mapper/3600a09807770457a795d526950374c76", devicePath) +} + +func TestGetUnderlyingDevicePathForLUKSDevice_FailsWithBadOutput(t *testing.T) { + ctx := context.Background() + execReturnValue := `bad output` + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) + + devicePath, err := GetUnderlyingDevicePathForLUKSDevice(ctx, mockCommand, "") + assert.Error(t, err) + assert.Equal(t, "", devicePath) +} + +func TestGetUnderlyingDevicePathForLUKSDevice_FailsWithNoLUKSDevice(t *testing.T) { + ctx := context.Background() + execReturnValue := `/dev/mapper/luks-trident_pvc_0c6202cb_be41_46b7_bea9_7f2c5c2c4a41 is active and is in use. + type: LUKS2 + cipher: aes-xts-plain64 + keysize: 512 bits + key location: keyring + device: + sector size: 512 + offset: 32768 sectors + size: 2064384 sectors + mode: read/write` + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) + + devicePath, err := GetUnderlyingDevicePathForLUKSDevice(ctx, mockCommand, "") + assert.Error(t, err) + assert.Equal(t, "", devicePath) +} + +func TestGetUnderlyingDevicePathForLUKSDevice_FailsWithDeviceIncorrectlyFormatted(t *testing.T) { + ctx := context.Background() + execReturnValue := `/dev/mapper/luks-trident_pvc_0c6202cb_be41_46b7_bea9_7f2c5c2c4a41 is active and is in use. + type: LUKS2 + cipher: aes-xts-plain64 + keysize: 512 bits + key location: keyring + device: /dev/mapper/3600a09807770457a795d526950374c76 + sector size: 512 + offset: 32768 sectors + size: 2064384 sectors + mode: read/write` + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) + + _, err := GetUnderlyingDevicePathForLUKSDevice(ctx, mockCommand, "") + assert.Error(t, err) +} + +func TestCheckPassphrase_Succeeds(t *testing.T) { + ctx := context.Background() + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + + mockCryptsetupLuksTestPassphrase(mockCommand).Return([]byte(""), nil) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"test-pvc", mockCommand, nil, nil) + + correct, err := luksDevice.CheckPassphrase(ctx, "passphrase") + assert.True(t, correct) + assert.NoError(t, err) +} + +func TestCheckPassphrase_FailsWithExecError(t *testing.T) { + ctx := context.Background() + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksTestPassphrase(mockCommand).Return([]byte(""), luksError) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"test-pvc", mockCommand, nil, nil) + + correct, err := luksDevice.CheckPassphrase(ctx, "passphrase") + assert.False(t, correct) + assert.Error(t, err) +} + +func TestCheckPassphrase_DetectsPassphraseIsBad(t *testing.T) { + ctx := context.Background() + fakeExitError := mock_exec.NewMockExitError(2, "mock error") // Exit code of 2 means the passphrase was bad. + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksTestPassphrase(mockCommand).Return([]byte(""), fakeExitError) + + luksDevice := NewDetailed("/dev/sdb", luksDevicePrefix+"test-pvc", mockCommand, nil, nil) + + correct, err := luksDevice.CheckPassphrase(ctx, "passphrase") + assert.False(t, correct) + assert.NoError(t, err) +} + +func TestNewLUKSDeviceFromMappingPath_Succeeds(t *testing.T) { + ctx := context.Background() + execReturnValue := `/dev/mapper/luks-pvc-test is active and is in use. + type: LUKS2 + cipher: aes-xts-plain64 + keysize: 512 bits + key location: keyring + device: /dev/sdb + sector size: 512 + offset: 32768 sectors + size: 2064384 sectors + mode: read/write` + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) + + luksDevice, err := NewLUKSDeviceFromMappingPath(ctx, mockCommand, "/dev/mapper/luks-pvc-test", "pvc-test") + assert.NoError(t, err) + assert.Equal(t, luksDevice.RawDevicePath(), "/dev/sdb") + assert.Equal(t, luksDevice.MappedDevicePath(), "/dev/mapper/luks-pvc-test") + assert.Equal(t, luksDevice.MappedDeviceName(), "luks-pvc-test") +} + +func TestNewLUKSDeviceFromMappingPath_Fails(t *testing.T) { + ctx := context.Background() + execReturnValue := `bad output` + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) + + luksDevice, err := NewLUKSDeviceFromMappingPath(ctx, mockCommand, "/dev/mapper/luks-pvc-test", "pvc-test") + assert.Error(t, err) + assert.Nil(t, luksDevice) +} + +func TestResize_Succeeds(t *testing.T) { + ctx := context.Background() + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksResize(mockCommand).Return([]byte(""), nil) + + luksDeviceName := "luks-test_pvc" + luksDevice := NewDetailed("/dev/sdb", luksDeviceName, mockCommand, devices.New(), afero.NewMemMapFs()) + err := luksDevice.Resize(ctx, "testpassphrase") + assert.NoError(t, err) +} + +func TestResize_FailsWithBadPassphrase(t *testing.T) { + ctx := context.Background() + fakeExitError := mock_exec.NewMockExitError(2, "mock error") // Exit code of 2 means the passphrase was bad. + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksResize(mockCommand).Return([]byte(""), fakeExitError) + + luksDeviceName := "luks-test_pvc" + luksDevice := NewDetailed("/dev/sdb", luksDeviceName, mockCommand, devices.New(), afero.NewMemMapFs()) + err := luksDevice.Resize(ctx, "testpassphrase") + assert.Error(t, err) + + expectedError := errors.IncorrectLUKSPassphraseError("") + assert.ErrorAs(t, err, &expectedError) +} + +func TestResize_FailsWithExecError(t *testing.T) { + ctx := context.Background() + fakeExitError := mock_exec.NewMockExitError(4, "mock error") + + mockCtrl := gomock.NewController(t) + mockCommand := mockexec.NewMockCommand(mockCtrl) + mockCryptsetupLuksResize(mockCommand).Return([]byte(""), fakeExitError) + + luksDeviceName := "luks-test_pvc" + luksDevice := NewDetailed("/dev/sdb", luksDeviceName, mockCommand, devices.New(), afero.NewMemMapFs()) + err := luksDevice.Resize(ctx, "testpassphrase") + assert.Error(t, err) + + unexpectedError := errors.IncorrectLUKSPassphraseError("") + assert.NotErrorIs(t, err, unexpectedError) +} + +func TestEnsureFormattedAndOpen(t *testing.T) { + // Positive case: Test first passphrase works + passphrase := "secretA" + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + mockCryptsetupLuksStatus(mockCommand) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + formatted, err := luksDevice.EnsureFormattedAndOpen(context.Background(), passphrase) + assert.True(t, formatted) + assert.NoError(t, err) +} + +func TestMountLUKSDevice_firstPassphraseSuccess(t *testing.T) { + // Positive case: Test first passphrase works + secrets := map[string]string{"luks-passphrase": "secretA", "luks-passphrase-name": "A"} + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, + mock_exec.NewMockExitError(cryptsetupStatusDeviceDoesNotExistStatusCode, "mock error")) + mockCryptsetupIsLuks(mockCommand).Return([]byte{}, nil) + mockCryptsetupLuksOpen(mockCommand).Return([]byte{}, nil) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.NoError(t, err) + assert.True(t, luksFormatted) +} + +func TestMountLUKSDevice_secondPassphraseSuccess(t *testing.T) { + // Positive case: Test second passphrase works + secrets := map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + "previous-luks-passphrase": "secretB", + "previous-luks-passphrase-name": "B", + } + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, fmt.Errorf("mock error")) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, + mock_exec.NewMockExitError(cryptsetupStatusDeviceDoesNotExistStatusCode, "mock error")) + mockCryptsetupIsLuks(mockCommand).Return([]byte{}, nil) + mockCryptsetupLuksOpen(mockCommand).Return([]byte{}, nil) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.NoError(t, err) + assert.True(t, luksFormatted) +} + +func TestMountLUKSDevice_passphraseRotationFails(t *testing.T) { + // Negative case: passphrase rotation fails + secrets := map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + "previous-luks-passphrase": "secretB", + "previous-luks-passphrase-name": "B", + } + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, fmt.Errorf("mock error")).Times(2) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestMountLUKSDevice_NoPassphraseFailure(t *testing.T) { + // Negative case: Test no passphrase specified + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + + secrets := map[string]string{} + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestMountLUKSDevice_NoPassphraseNameFailure(t *testing.T) { + // Negative case: Test no passphrase name specified + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + + secrets := map[string]string{ + "luks-passphrase": "secretA", + } + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestMountLUKSDevice_NoSecondPassphraseNameFailure(t *testing.T) { + // Negative case: Test no second passphrase name specified + secrets := map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + "previous-luks-passphrase": "secretB", + } + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, fmt.Errorf("mock error")).Times(1) + + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestMountLUKSDevice_NoSecondPassphraseNameSpecifiedFailure(t *testing.T) { + // // Negative case: Test first passphrase fails, no second specified + + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, fmt.Errorf("mock error")) + + secrets := map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + } + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestMountLUKSDevice_NoSecondPassphraseNameBlankFailure(t *testing.T) { + // Negative case: Test first passphrase fails, second is blank + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, fmt.Errorf("mock error")) + secrets := map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + "previous-luks-passphrase": "", + "previous-luks-passphrase-name": "", + } + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestMountLUKSDevice_DuplicatePassphraseFailure(t *testing.T) { + // Negative case: Test first passphrase fails, second is the same + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, fmt.Errorf("mock-error")) + secrets := map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + "previous-luks-passphrase": "secretA", + "previous-luks-passphrase-name": "A", + } + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestMountLUKSDevice_FirstPassphraseBlankFailure(t *testing.T) { + // Negative case: Test first passphrase is blank + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + secrets := map[string]string{ + "luks-passphrase": "", + "luks-passphrase-name": "", + "previous-luks-passphrase": "secretB", + "previous-luks-passphrase-name": "B", + } + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestMountLUKSDevice_FirstPassphraseBlankFailureasdf(t *testing.T) { + // Negative case: Test second passphrase is also incorrect + secrets := map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + "previous-luks-passphrase": "secretB", + "previous-luks-passphrase-name": "B", + } + mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) + luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) + mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, fmt.Errorf("mock-error")).Times(2) + + luksFormatted, err := luksDevice.EnsureLUKSDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.Error(t, err) + assert.False(t, luksFormatted) +} + +func TestGenerateAnonymousMemFile(t *testing.T) { + tempFileName := "testFile" + content := "testContent" + + fd, err := generateAnonymousMemFile(tempFileName, content) + assert.NoError(t, err, "expected no error creating anonymous mem file") + assert.Greater(t, fd, 0, "expected valid file descriptor") + + // Read back the content to verify + readContent := make([]byte, len(content)) + _, err = unix.Read(fd, readContent) + assert.NoError(t, err, "expected no error reading anonymous mem file") + assert.Equal(t, content, string(readContent), "expected content to match") + + // Close the file descriptor + err = unix.Close(fd) + assert.NoError(t, err, "expected no error closing anonymous mem file") +} diff --git a/utils/devices/luks/luks_test.go b/utils/devices/luks/luks_test.go new file mode 100644 index 000000000..d38a6a518 --- /dev/null +++ b/utils/devices/luks/luks_test.go @@ -0,0 +1,16 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +package luks + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewLUKSDevice(t *testing.T) { + luksDevice := NewLUKSDevice("/dev/sdb", "pvc-test", nil) + assert.Equal(t, luksDevice.RawDevicePath(), "/dev/sdb") + assert.Equal(t, luksDevice.MappedDevicePath(), "/dev/mapper/luks-pvc-test") + assert.Equal(t, luksDevice.MappedDeviceName(), "luks-pvc-test") +} diff --git a/utils/devices/luks/luks_windows.go b/utils/devices/luks/luks_windows.go new file mode 100644 index 000000000..547863c37 --- /dev/null +++ b/utils/devices/luks/luks_windows.go @@ -0,0 +1,73 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +package luks + +import ( + "golang.org/x/net/context" + + . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/exec" +) + +func (d *LUKSDevice) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) { + Logc(ctx).Debug(">>>> devices_windows.CheckPassphrase") + defer Logc(ctx).Debug("<<<< devices_windows.CheckPassphrase") + return false, errors.UnsupportedError("CheckPassphrase is not supported for windows") +} + +func (d *LUKSDevice) RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error { + Logc(ctx).Debug(">>>> devices_windows.RotatePassphrase") + defer Logc(ctx).Debug("<<<< devices_windows.RotatePassphrase") + return errors.UnsupportedError("RotatePassphrase is not supported for windows") +} + +// IsLUKSFormatted returns whether LUKS headers have been placed on the device +func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { + Logc(ctx).Debug(">>>> devices_windows.IsLUKSFormatted") + defer Logc(ctx).Debug("<<<< devices_windows.IsLUKSFormatted") + return false, errors.UnsupportedError("IsLUKSFormatted is not supported for windows") +} + +// IsOpen returns whether the device is an active luks device on the host +func (d *LUKSDevice) IsOpen(ctx context.Context) (bool, error) { + Logc(ctx).Debug(">>>> devices_windows.IsOpen") + defer Logc(ctx).Debug("<<<< devices_windows.IsOpen") + return false, errors.UnsupportedError("IsOpen is not supported for windows") +} + +// LUKSFormat attempts to set up LUKS headers on a device with the specified passphrase, but bails if the +// underlying device already has a format present that is not LUKS. +func (d *LUKSDevice) lUKSFormat(ctx context.Context, _ string) error { + Logc(ctx).Debug(">>>> devices_windows.LUKSFormat") + defer Logc(ctx).Debug("<<<< devices_windows.LUKSFormat") + return errors.UnsupportedError("LUKSFormat is not supported for windows") +} + +// Open makes the device accessible on the host +func (d *LUKSDevice) Open(ctx context.Context, luksPassphrase string) error { + Logc(ctx).Debug(">>>> devices_windows.Open") + defer Logc(ctx).Debug("<<<< devices_windows.Open") + return errors.UnsupportedError("Open is not supported for windows") +} + +// Resize performs a luksResize on the LUKS device +func (d *LUKSDevice) Resize(ctx context.Context, luksPassphrase string) error { + Logc(ctx).Debug(">>>> devices_darwin.Resize") + defer Logc(ctx).Debug("<<<< devices_darwin.Resize") + return errors.UnsupportedError("Resize is not supported for darwin") +} + +func GetUnderlyingDevicePathForLUKSDevice(ctx context.Context, command exec.Command, luksDevicePath string) (string, error) { + Logc(ctx).Debug(">>>> devices_windows.GetUnderlyingDevicePathForLUKSDevice") + defer Logc(ctx).Debug("<<<< devices_windows.GetUnderlyingDevicePathForLUKSDevice") + return "", errors.UnsupportedError("GetUnderlyingDevicePathForLUKSDevice is not supported for windows") +} + +func IsOpen(ctx context.Context, devicePath string) (bool, error) { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + + Logc(ctx).Debug(">>>> devices_windows.IsOpen") + defer Logc(ctx).Debug("<<<< devices_windows.IsOpen") + return false, errors.UnsupportedError("IsOpen is not supported for windows") +} diff --git a/utils/devices/utils.go b/utils/devices/utils.go new file mode 100644 index 000000000..6211e27c4 --- /dev/null +++ b/utils/devices/utils.go @@ -0,0 +1,14 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +package devices + +import ( + "os" +) + +func PathExists(path string) (bool, error) { + if _, err := os.Stat(path); err == nil { + return true, nil + } + return false, nil +} diff --git a/utils/devices_darwin.go b/utils/devices_darwin.go deleted file mode 100644 index 11121a904..000000000 --- a/utils/devices_darwin.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. - -// NOTE: This file should only contain functions for handling devices for Darwin flavor - -package utils - -import ( - "golang.org/x/net/context" - - . "github.com/netapp/trident/logging" - "github.com/netapp/trident/utils/errors" -) - -// flushOneDevice unused stub function -func flushOneDevice(ctx context.Context, devicePath string) error { - Logc(ctx).Debug(">>>> devices_darwin.flushOneDevice") - defer Logc(ctx).Debug("<<<< devices_darwin.flushOneDevice") - return errors.UnsupportedError("flushOneDevice is not supported for darwin") -} - -// getISCSIDiskSize unused stub function -func getISCSIDiskSize(ctx context.Context, _ string) (int64, error) { - Logc(ctx).Debug(">>>> devices_darwin.getISCSIDiskSize") - defer Logc(ctx).Debug("<<<< devices_darwin.getISCSIDiskSize") - return 0, errors.UnsupportedError("getBlockSize is not supported for darwin") -} - -func IsOpenLUKSDevice(ctx context.Context, devicePath string) (bool, error) { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - Logc(ctx).Debug(">>>> devices_darwin.IsLUKSDeviceOpen") - defer Logc(ctx).Debug("<<<< devices_darwin.IsLUKSDeviceOpen") - return false, errors.UnsupportedError("IsLUKSDeviceOpen is not supported for darwin") -} - -func EnsureLUKSDeviceClosed(ctx context.Context, luksDevicePath string) error { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - Logc(ctx).Debug(">>>> devices_darwin.EnsureLUKSDeviceClosed") - defer Logc(ctx).Debug("<<<< devices_darwin.EnsureLUKSDeviceClosed") - return errors.UnsupportedError("EnsureLUKSDeviceClosed is not supported for darwin") -} - -func getDeviceFSType(ctx context.Context, device string) (string, error) { - Logc(ctx).Debug(">>>> devices_darwin.getDeviceFSType") - defer Logc(ctx).Debug("<<<< devices_darwin.getDeviceFSType") - return "", errors.UnsupportedError("getDeviceFSTypeis not supported for darwin") -} - -func getDeviceFSTypeRetry(ctx context.Context, device string) (string, error) { - Logc(ctx).Debug(">>>> devices_darwin.getDeviceFSTypeRetry") - defer Logc(ctx).Debug("<<<< devices_darwin.getDeviceFSTypeRetry") - return "", errors.UnsupportedError("getDeviceFSTypeRetry is not supported for darwin") -} - -// IsLUKSFormatted returns whether LUKS headers have been placed on the device -func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { - Logc(ctx).Debug(">>>> devices_darwin.IsLUKSFormatted") - defer Logc(ctx).Debug("<<<< devices_darwin.IsLUKSFormatted") - return false, errors.UnsupportedError("IsLUKSFormatted is not supported for darwin") -} - -// IsOpen returns whether the device is an active luks device on the host -func (d *LUKSDevice) IsOpen(ctx context.Context) (bool, error) { - Logc(ctx).Debug(">>>> devices_darwin.IsOpen") - defer Logc(ctx).Debug("<<<< devices_darwin.IsOpen") - return false, errors.UnsupportedError("IsOpen is not supported for darwin") -} - -// LUKSFormat attempts to set up LUKS headers on a device with the specified passphrase, but bails if the -// underlying device already has a format present that is not LUKS. -func (d *LUKSDevice) LUKSFormat(ctx context.Context, _ string) error { - Logc(ctx).Debug(">>>> devices_darwin.LUKSFormat") - defer Logc(ctx).Debug("<<<< devices_darwin.LUKSFormat") - return errors.UnsupportedError("LUKSFormat is not supported for darwin") -} - -// Open makes the device accessible on the host -func (d *LUKSDevice) Open(ctx context.Context, luksPassphrase string) error { - Logc(ctx).Debug(">>>> devices_darwin.Open") - defer Logc(ctx).Debug("<<<< devices_darwin.Open") - return errors.UnsupportedError("Open is not supported for darwin") -} - -// Close performs a luksClose on the LUKS device -func (d *LUKSDevice) Close(ctx context.Context) error { - Logc(ctx).Debug(">>>> devices_darwin.Close") - defer Logc(ctx).Debug("<<<< devices_darwin.Close") - return errors.UnsupportedError("Close is not supported for darwin") -} - -func (d *LUKSDevice) RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error { - Logc(ctx).Debug(">>>> devices_darwin.RotatePassphrase") - defer Logc(ctx).Debug("<<<< devices_darwin.RotatePassphrase") - return errors.UnsupportedError("RotatePassphrase is not supported for darwin") -} - -func (d *LUKSDevice) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) { - Logc(ctx).Debug(">>>> devices_darwin.CheckPassphrase") - defer Logc(ctx).Debug("<<<< devices_darwin.CheckPassphrase") - return false, errors.UnsupportedError("CheckPassphrase is not supported for darwin") -} - -func GetUnderlyingDevicePathForLUKSDevice(ctx context.Context, luksDevicePath string) (string, error) { - Logc(ctx).Debug(">>>> devices_darwin.GetUnderlyingDevicePathForLUKSDevice") - defer Logc(ctx).Debug("<<<< devices_darwin.GetUnderlyingDevicePathForLUKSDevice") - return "", errors.UnsupportedError("GetUnderlyingDevicePathForLUKSDevice is not supported for darwin") -} - -// Resize performs a luksResize on the LUKS device -func (d *LUKSDevice) Resize(ctx context.Context, luksPassphrase string) error { - Logc(ctx).Debug(">>>> devices_darwin.Resize") - defer Logc(ctx).Debug("<<<< devices_darwin.Resize") - return errors.UnsupportedError("Resize is not supported for darwin") -} - -func EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { - Logc(ctx).Debug(">>>> devices_darwin.EnsureLUKSDeviceClosedWithMaxWaitLimit") - defer Logc(ctx).Debug("<<<< devices_darwin.EnsureLUKSDeviceClosedWithMaxWaitLimit") - return errors.UnsupportedError("EnsureLUKSDeviceClosedWithMaxWaitLimit is not supported for darwin") -} - -func closeLUKSDevice(ctx context.Context, devicePath string) error { - Logc(ctx).Debug(">>>> devices_darwin.closeLUKSDevice") - defer Logc(ctx).Debug("<<<< devices_darwin.closeLUKSDevice") - return errors.UnsupportedError("closeLUKSDevice is not supported for darwin") -} - -func getFCPDiskSize(ctx context.Context, devicePath string) (int64, error) { - Logc(ctx).Debug(">>>> devices_darwin.getFCPDiskSize") - defer Logc(ctx).Debug("<<<< devices_darwin.getFCPDiskSize") - return 0, errors.UnsupportedError("getFCPDiskSize is not supported for darwin") -} diff --git a/utils/devices_linux_test.go b/utils/devices_linux_test.go deleted file mode 100644 index a2dda143e..000000000 --- a/utils/devices_linux_test.go +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. - -//go:build linux - -package utils - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "go.uber.org/mock/gomock" - - mockexec "github.com/netapp/trident/mocks/mock_utils/mock_exec" - mockluks "github.com/netapp/trident/mocks/mock_utils/mock_models/mock_luks" - "github.com/netapp/trident/utils/errors" - "github.com/netapp/trident/utils/exec" -) - -var luksError = fmt.Errorf("luks error") - -func mockCryptsetupIsLuks(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, "", "isLuks", gomock.Any(), - ) -} - -func mockCryptsetupLuksFormat(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, gomock.Any(), - "luksFormat", gomock.Any(), "--type", "luks2", "-c", "aes-xts-plain64", - ) -} - -func mockCryptsetupLuksStatus(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, "", "status", gomock.Any(), - ) -} - -func mockCryptsetupLuksOpen(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, gomock.Any(), - "open", gomock.Any(), gomock.Any(), "--type", "luks2", - ) -} - -func mockCryptsetupLuksClose(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, "", "luksClose", gomock.Any(), - ) -} - -// mockDiscDump mocks out the "dd" binary executed via command interface. See devices.go#isDeviceUnformatted. -func mockDiscDump(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeout( - gomock.Any(), "dd", deviceOperationsTimeout, false, - gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - ) -} - -func mockCryptsetupLuksStatusWithDevicePath(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, "", "status", gomock.Any(), - ) -} - -func mockCryptsetupLuksTestPassphrase(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, gomock.Any(), "open", gomock.Any(), - gomock.Any(), "--type", "luks2", "--test-passphrase", - ) -} - -func mockCryptsetupLuksChangeKey(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, gomock.Any(), - "luksChangeKey", "-d", gomock.Any(), gomock.Any(), - ) -} - -func mockCryptsetupLuksResize(mock *mockexec.MockCommand) *gomock.Call { - return mock.EXPECT().ExecuteWithTimeoutAndInput( - gomock.Any(), "cryptsetup", luksCommandTimeout, true, - gomock.Any(), "resize", gomock.Any(), - ) -} - -func TestLUKSDevice_IsLUKSFormatted(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) - assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) - - // Setup mock calls and reassign any clients to their mock counterparts. - mockCryptsetupIsLuks(mockCommand).Return([]byte(""), nil) - command = mockCommand - - isFormatted, err := luksDevice.IsLUKSFormatted(context.Background()) - assert.NoError(t, err) - assert.True(t, isFormatted) -} - -func TestLUKSDevice_LUKSFormat(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) - assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) - - // Setup mock calls and reassign any clients to their mock counterparts. - mockCryptsetupIsLuks(mockCommand).Return([]byte(""), nil) - command = mockCommand - - err := luksDevice.LUKSFormat(context.Background(), "passphrase") - assert.NoError(t, err) -} - -func TestLUKSDevice_LUKSFormat_FailsCheckingIfDeviceIsLUKS(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - // Create fake luks device. - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) - assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) - - // Mock any cryptsetup calls that may occur. - mockCryptsetupIsLuks(mockCommand).Return([]byte(""), luksError) - command = mockCommand - - err := luksDevice.LUKSFormat(ctx, "mysecretlukspassphrase") - assert.Error(t, err) -} - -func TestLUKSDevice_Open(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) - assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) - - // Setup mock calls and reassign any clients to their mock counterparts. - mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), nil) - command = mockCommand - - err := luksDevice.Open(context.Background(), "passphrase") - assert.NoError(t, err) -} - -func TestLUKSDevice_IsOpen(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) - assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) - - // Setup mock calls and reassign any clients to their mock counterparts. - mockCryptsetupLuksStatus(mockCommand).Return([]byte(""), nil) - command = mockCommand - - isOpen, err := luksDevice.IsOpen(context.Background()) - assert.NoError(t, err) - assert.True(t, isOpen) -} - -func TestLUKSDevice_Close(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - assert.Equal(t, "/dev/mapper/pvc-test", luksDevice.MappedDevicePath()) - assert.Equal(t, "/dev/sdb", luksDevice.RawDevicePath()) - - // Setup mock calls and reassign any clients to their mock counterparts. - mockCryptsetupLuksClose(mockCommand).Return([]byte(""), nil) - command = mockCommand - - err := luksDevice.Close(context.Background()) - assert.NoError(t, err) -} - -func TestLUKSDevice_MissingDevicePath(t *testing.T) { - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: ""} - isFormatted, err := luksDevice.IsLUKSFormatted(context.Background()) - assert.Error(t, err) - assert.False(t, isFormatted) - - err = luksDevice.luksFormat(context.Background(), "passphrase") - assert.Error(t, err) - - err = luksDevice.Open(context.Background(), "passphrase") - assert.Error(t, err) -} - -func TestLUKSDevice_ExecErrors(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - - // Setup mock calls and reassign any clients to their mock counterparts. - gomock.InOrder( - mockCryptsetupIsLuks(mockCommand).Return([]byte(""), luksError), - mockCryptsetupLuksFormat(mockCommand).Return([]byte(""), luksError), - mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), luksError), - mockCryptsetupLuksStatus(mockCommand).Return([]byte(""), luksError), - mockCryptsetupLuksClose(mockCommand).Return([]byte(""), luksError), - ) - command = mockCommand - - isFormatted, err := luksDevice.IsLUKSFormatted(context.Background()) - assert.Error(t, err) - assert.False(t, isFormatted) - - err = luksDevice.luksFormat(context.Background(), "passphrase") - assert.Error(t, err) - - err = luksDevice.Open(context.Background(), "passphrase") - assert.Error(t, err) - - isOpen, err := luksDevice.IsOpen(context.Background()) - assert.Error(t, err) - assert.False(t, isOpen) - - err = luksDevice.Close(context.Background()) - assert.Error(t, err) -} - -func TestEnsureLUKSDevice_FailsWithExecError(t *testing.T) { - ctx := context.Background() - mockCtrl := gomock.NewController(t) - mockLUKSDevice := mockluks.NewMockLUKSDeviceInterface(mockCtrl) - - // Setup mock calls and reassign any clients to their mock counterparts. - mockLUKSDevice.EXPECT().IsOpen(ctx).Return(false, luksError) - - luksFormatted, err := ensureLUKSDevice(context.Background(), mockLUKSDevice, "mysecretlukspassphrase") - assert.Error(t, err) - assert.Equal(t, false, luksFormatted) -} - -func TestEnsureLUKSDevice_IsOpen(t *testing.T) { - ctx := context.Background() - mockCtrl := gomock.NewController(t) - mockLUKSDevice := mockluks.NewMockLUKSDeviceInterface(mockCtrl) - - // Setup mock calls and reassign any clients to their mock counterparts. - mockLUKSDevice.EXPECT().IsOpen(ctx).Return(true, nil) - - luksFormatted, err := ensureLUKSDevice(context.Background(), mockLUKSDevice, "mysecretlukspassphrase") - assert.NoError(t, err) - assert.True(t, luksFormatted) -} - -func TestEnsureLUKSDevice_LUKSFormatFails(t *testing.T) { - ctx := context.Background() - mockCtrl := gomock.NewController(t) - mockLUKSDevice := mockluks.NewMockLUKSDeviceInterface(mockCtrl) - - // Setup mock calls and reassign any clients to their mock counterparts. - mockLUKSDevice.EXPECT().IsOpen(ctx).Return(false, nil) - mockLUKSDevice.EXPECT().LUKSFormat(ctx, "mysecretlukspassphrase").Return(luksError) - - luksFormatted, err := ensureLUKSDevice(context.Background(), mockLUKSDevice, "mysecretlukspassphrase") - assert.Error(t, err) - assert.False(t, luksFormatted) -} - -func TestEnsureLUKSDeviceClosed_DeviceDoesNotExist(t *testing.T) { - defer func() { - osFs = afero.NewOsFs() - }() - - osFs = afero.NewMemMapFs() - err := EnsureLUKSDeviceClosed(context.Background(), "/dev/mapper/luks-test-dev") - assert.NoError(t, err) -} - -func TestEnsureLUKSDeviceClosed_FailsToDetectDevice(t *testing.T) { - defer func() { - osFs = afero.NewOsFs() - }() - - osFs = afero.NewOsFs() - var b strings.Builder - b.Grow(1025) - for i := 0; i < 1025; i++ { - b.WriteByte('a') - } - s := b.String() - err := EnsureLUKSDeviceClosed(context.Background(), "/dev/mapper/"+s) - assert.Error(t, err) -} - -func TestEnsureLUKSDeviceClosed_FailsToCloseDevice(t *testing.T) { - defer func() { - osFs = afero.NewOsFs() - }() - - ctx := context.Background() - devicePath := "/dev/mapper/luks-test-dev" - osFs = afero.NewMemMapFs() - osFs.Create(devicePath) - - err := EnsureLUKSDeviceClosed(ctx, devicePath) - assert.Error(t, err) -} - -func TestEnsureLUKSDeviceClosed_ClosesDevice(t *testing.T) { - defer func(previousCommand exec.Command) { - osFs = afero.NewOsFs() - command = previousCommand - }(command) - - ctx := context.Background() - devicePath := "/dev/mapper/luks-test-dev" - osFs = afero.NewMemMapFs() - osFs.Create(devicePath) - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - // Setup mock calls and reassign any clients to their mock counterparts. - gomock.InOrder( - mockCryptsetupLuksClose(mockCommand).Return([]byte(""), nil), - ) - command = mockCommand - - err := EnsureLUKSDeviceClosed(ctx, devicePath) - assert.NoError(t, err) -} - -func TestLUKSDeviceOpen_FailsWithBadPassphrase(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - fakeExitError := exec.NewFakeExitError(2, "") // Exit code of 2 means the passphrase was bad. - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), fakeExitError) - command = mockCommand - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - err := luksDevice.Open(ctx, "passphrase") - assert.Error(t, err) -} - -func TestLUKSDeviceOpen_MiscError(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - fakeExitError := exec.NewFakeExitError(2, "") // Exit code of 2 means the passphrase was bad. - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), fakeExitError) - command = mockCommand - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - err := luksDevice.Open(ctx, "passphrase") - assert.Error(t, err) -} - -func TestLUKSDeviceOpen_ExecError(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - fakeExitError := exec.NewFakeExitError(1, "") // Exit code of 1 means the exec command failed. - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksOpen(mockCommand).Return([]byte(""), fakeExitError) - command = mockCommand - - luksDevice := LUKSDevice{mappingName: "pvc-test", rawDevicePath: "/dev/sdb"} - err := luksDevice.Open(ctx, "passphrase") - assert.Error(t, err) - assert.ErrorContains(t, err, "could not open LUKS device; ") -} - -func TestRotateLUKSDevicePassphrase_NoError(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - luksDeviceName := "luks-pvc-test" - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksChangeKey(mockCommand).Return([]byte(""), nil) - command = mockCommand - - luksDevice := &LUKSDevice{"/dev/sdb", luksDeviceName} - err := luksDevice.RotatePassphrase(ctx, "pvc-test", "previous", "newpassphrase") - assert.NoError(t, err) -} - -func TestRotateLUKSDevicePassphrase_OldPassphraseEmpty(t *testing.T) { - ctx := context.Background() - luksDeviceName := "luks-pvc-test" - luksDevice := &LUKSDevice{"/dev/sdb", luksDeviceName} - err := luksDevice.RotatePassphrase(ctx, "pvc-test", "", "newpassphrase") - assert.Error(t, err) -} - -func TestRotateLUKSDevicePassphrase_NewPassphraseEmpty(t *testing.T) { - ctx := context.Background() - luksDeviceName := "luks-pvc-test" - luksDevice := &LUKSDevice{"/dev/sdb", luksDeviceName} - err := luksDevice.RotatePassphrase(ctx, "pvc-test", "oldpassphrase", "") - assert.Error(t, err) -} - -func TestRotateLUKSDevicePassphrase_CommandError(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksChangeKey(mockCommand).Return([]byte(""), luksError) - command = mockCommand - - ctx := context.Background() - luksDeviceName := "luks-pvc-test" - luksDevice := &LUKSDevice{"/dev/sdb", luksDeviceName} - err := luksDevice.RotatePassphrase(ctx, "pvc-test", "previous", "newpassphrase") - assert.Error(t, err) -} - -func TestRotateLUKSDevicePassphrase_NoRawDevicePath(t *testing.T) { - ctx := context.Background() - luksDeviceName := "luks-pvc-test" - luksDevice := &LUKSDevice{"", luksDeviceName} - err := luksDevice.RotatePassphrase(ctx, "pvc-test", "previous", "newpassphrase") - assert.Error(t, err) -} - -func TestGetUnderlyingDevicePathForLUKSDevice_Succeeds(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - execReturnValue = `/dev/mapper/luks-trident_pvc_0c6202cb_be41_46b7_bea9_7f2c5c2c4a41 is active and is in use. - type: LUKS2 - cipher: aes-xts-plain64 - keysize: 512 bits - key location: keyring - device: /dev/mapper/3600a09807770457a795d526950374c76 - sector size: 512 - offset: 32768 sectors - size: 2064384 sectors - mode: read/write` - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) - command = mockCommand - - devicePath, err := GetUnderlyingDevicePathForLUKSDevice(ctx, "") - assert.NoError(t, err) - assert.Equal(t, "/dev/mapper/3600a09807770457a795d526950374c76", devicePath) -} - -func TestGetUnderlyingDevicePathForLUKSDevice_FailsWithBadOutput(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - execReturnValue = `bad output` - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) - command = mockCommand - - devicePath, err := GetUnderlyingDevicePathForLUKSDevice(ctx, "") - assert.Error(t, err) - assert.Equal(t, "", devicePath) -} - -func TestGetUnderlyingDevicePathForLUKSDevice_FailsWithNoLUKSDevice(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - execReturnValue = `/dev/mapper/luks-trident_pvc_0c6202cb_be41_46b7_bea9_7f2c5c2c4a41 is active and is in use. - type: LUKS2 - cipher: aes-xts-plain64 - keysize: 512 bits - key location: keyring - device: - sector size: 512 - offset: 32768 sectors - size: 2064384 sectors - mode: read/write` - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) - command = mockCommand - - devicePath, err := GetUnderlyingDevicePathForLUKSDevice(ctx, "") - assert.Error(t, err) - assert.Equal(t, "", devicePath) -} - -func TestGetUnderlyingDevicePathForLUKSDevice_FailsWithDeviceIncorrectlyFormatted(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - execReturnValue = `/dev/mapper/luks-trident_pvc_0c6202cb_be41_46b7_bea9_7f2c5c2c4a41 is active and is in use. - type: LUKS2 - cipher: aes-xts-plain64 - keysize: 512 bits - key location: keyring - device: /dev/mapper/3600a09807770457a795d526950374c76 - sector size: 512 - offset: 32768 sectors - size: 2064384 sectors - mode: read/write` - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) - command = mockCommand - - _, err := GetUnderlyingDevicePathForLUKSDevice(ctx, "") - assert.Error(t, err) -} - -func TestCheckPassphrase_Succeeds(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - - mockCryptsetupLuksTestPassphrase(mockCommand).Return([]byte(""), nil) - command = mockCommand - - luksDevice, err := NewLUKSDevice("", "test-pvc") - assert.NoError(t, err) - - correct, err := luksDevice.CheckPassphrase(ctx, "passphrase") - assert.True(t, correct) - assert.NoError(t, err) -} - -func TestCheckPassphrase_FailsWithExecError(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksTestPassphrase(mockCommand).Return([]byte(""), luksError) - command = mockCommand - - luksDevice, err := NewLUKSDevice("", "test-pvc") - assert.NoError(t, err) - - correct, err := luksDevice.CheckPassphrase(ctx, "passphrase") - assert.False(t, correct) - assert.Error(t, err) -} - -func TestCheckPassphrase_DetectsPassphraseIsBad(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - fakeExitError := exec.NewFakeExitError(2, "") // Exit code of 2 means the passphrase was bad. - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksTestPassphrase(mockCommand).Return([]byte(""), fakeExitError) - command = mockCommand - - luksDevice, err := NewLUKSDevice("", "test-pvc") - assert.NoError(t, err) - - correct, err := luksDevice.CheckPassphrase(ctx, "passphrase") - assert.False(t, correct) - assert.NoError(t, err) -} - -func TestNewLUKSDeviceFromMappingPath_Succeeds(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - execReturnValue = `/dev/mapper/luks-pvc-test is active and is in use. - type: LUKS2 - cipher: aes-xts-plain64 - keysize: 512 bits - key location: keyring - device: /dev/sdb - sector size: 512 - offset: 32768 sectors - size: 2064384 sectors - mode: read/write` - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) - command = mockCommand - - luksDevice, err := NewLUKSDeviceFromMappingPath(ctx, "/dev/mapper/luks-pvc-test", "pvc-test") - assert.NoError(t, err) - assert.Equal(t, luksDevice.RawDevicePath(), "/dev/sdb") - assert.Equal(t, luksDevice.MappedDevicePath(), "/dev/mapper/luks-pvc-test") - assert.Equal(t, luksDevice.MappedDeviceName(), "luks-pvc-test") -} - -func TestNewLUKSDeviceFromMappingPath_Fails(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - execReturnValue = `bad output` - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksStatusWithDevicePath(mockCommand).Return([]byte(execReturnValue), nil) - command = mockCommand - - luksDevice, err := NewLUKSDeviceFromMappingPath(ctx, "/dev/mapper/luks-pvc-test", "pvc-test") - assert.Error(t, err) - assert.Nil(t, luksDevice) -} - -func TestResize_Succeeds(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksResize(mockCommand).Return([]byte(""), nil) - command = mockCommand - - luksDeviceName := "luks-test_pvc" - luksDevice := &LUKSDevice{"/dev/sdb", luksDeviceName} - err := luksDevice.Resize(ctx, "testpassphrase") - assert.NoError(t, err) -} - -func TestResize_FailsWithBadPassphrase(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - fakeExitError := exec.NewFakeExitError(2, "") // Exit code of 2 means the passphrase was bad. - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksResize(mockCommand).Return([]byte(""), fakeExitError) - command = mockCommand - - luksDeviceName := "luks-test_pvc" - luksDevice := &LUKSDevice{"/dev/sdb", luksDeviceName} - err := luksDevice.Resize(ctx, "testpassphrase") - assert.Error(t, err) - - expectedError := errors.IncorrectLUKSPassphraseError("") - assert.ErrorAs(t, err, &expectedError) -} - -func TestResize_FailsWithExecError(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - ctx := context.Background() - fakeExitError := exec.NewFakeExitError(4, "") - - mockCtrl := gomock.NewController(t) - mockCommand := mockexec.NewMockCommand(mockCtrl) - mockCryptsetupLuksResize(mockCommand).Return([]byte(""), fakeExitError) - command = mockCommand - - luksDeviceName := "luks-test_pvc" - luksDevice := &LUKSDevice{"/dev/sdb", luksDeviceName} - err := luksDevice.Resize(ctx, "testpassphrase") - assert.Error(t, err) - - unexpectedError := errors.IncorrectLUKSPassphraseError("") - assert.NotErrorIs(t, err, unexpectedError) -} - -func TestEnsureLUKSDeviceClosedWithMaxWaitLimit(t *testing.T) { - defer func(previousCommand exec.Command) { - command = previousCommand - }(command) - - defer func() { - osFs = afero.NewOsFs() - }() - - osFs = afero.NewMemMapFs() - luksDevicePath := "/dev/mapper/luks-test" - osFs.Create(luksDevicePath) - client := mockexec.NewMockCommand(gomock.NewController(t)) - command = client // Set package var to mock - - type testCase struct { - name string - mockSetup func(*mockexec.MockCommand) - expectedError bool - expectedErrType error - } - - testCases := []testCase{ - { - name: "SucceedsWhenDeviceIsClosed", - mockSetup: func(mockCommand *mockexec.MockCommand) { - mockCryptsetupLuksClose(mockCommand).Return([]byte(""), nil) - }, - expectedError: false, - }, - { - name: "FailsBeforeMaxWaitLimit", - mockSetup: func(mockCommand *mockexec.MockCommand) { - mockCryptsetupLuksClose(mockCommand).Return([]byte(""), fmt.Errorf("close error")) - }, - expectedError: true, - expectedErrType: fmt.Errorf("%w", errors.New("")), - }, - { - name: "FailsWithMaxWaitExceededError", - mockSetup: func(mockCommand *mockexec.MockCommand) { - mockCryptsetupLuksClose(mockCommand).Return([]byte(""), fmt.Errorf("close error")) - LuksCloseDurations[luksDevicePath] = time.Now().Add(-luksCloseMaxWaitDuration - time.Second) - }, - expectedError: true, - expectedErrType: errors.MaxWaitExceededError(""), - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - tc.mockSetup(client) - err := EnsureLUKSDeviceClosedWithMaxWaitLimit(context.TODO(), luksDevicePath) - if tc.expectedError { - assert.Error(t, err) - if tc.expectedErrType != nil { - assert.IsType(t, tc.expectedErrType, err) - } - } else { - assert.NoError(t, err) - } - }) - } -} diff --git a/utils/devices_test.go b/utils/devices_test.go deleted file mode 100644 index 100edb256..000000000 --- a/utils/devices_test.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. - -package utils - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "go.uber.org/mock/gomock" - - mockexec "github.com/netapp/trident/mocks/mock_utils/mock_exec" - "github.com/netapp/trident/mocks/mock_utils/mock_models/mock_luks" - "github.com/netapp/trident/utils/errors" -) - -// //////////////////////////////////////////////////////////////////////////////////////////////////////////// -func TestNewLUKSDevice(t *testing.T) { - luksDevice, _ := NewLUKSDevice("/dev/sdb", "pvc-test") - - assert.Equal(t, luksDevice.RawDevicePath(), "/dev/sdb") - assert.Equal(t, luksDevice.MappedDevicePath(), "/dev/mapper/luks-pvc-test") - assert.Equal(t, luksDevice.MappedDeviceName(), "luks-pvc-test") -} - -// //////////////////////////////////////////////////////////////////////////////////////////////////////////// -func TestMountLUKSDevice(t *testing.T) { - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Positive case: Test first passphrase works - secrets := map[string]string{"luks-passphrase": "secretA", "luks-passphrase-name": "A"} - mockCtrl := gomock.NewController(t) - mockLUKSDevice := mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, nil) - - luksFormatted, err := EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.NoError(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Positive case: Test second passphrase works - secrets = map[string]string{ - "luks-passphrase": "secretA", - "luks-passphrase-name": "A", - "previous-luks-passphrase": "secretB", - "previous-luks-passphrase-name": "B", - } - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, fmt.Errorf("bad passphrase")) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretB").Return(false, nil) - - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.NoError(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: passphrase rotation fails - secrets = map[string]string{ - "luks-passphrase": "secretA", - "luks-passphrase-name": "A", - "previous-luks-passphrase": "secretB", - "previous-luks-passphrase-name": "B", - } - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, fmt.Errorf("bad passphrase")) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretB").Return(false, nil) - - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.NoError(t, err) - assert.False(t, luksFormatted) -} - -// //////////////////////////////////////////////////////////////////////////////////////////////////////////// -func TestMountLUKSDevice_Negative(t *testing.T) { - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Test no passphrase specified - mockCtrl := gomock.NewController(t) - mockLUKSDevice := mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - - secrets := map[string]string{} - luksFormatted, err := EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.Error(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Test no passphrase name specified - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - - secrets = map[string]string{ - "luks-passphrase": "secretA", - } - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.Error(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Test no second passphrase name specified - secrets = map[string]string{ - "luks-passphrase": "secretA", - "luks-passphrase-name": "A", - "previous-luks-passphrase": "secretB", - } - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, fmt.Errorf("bad passphrase")) - - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.Error(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Test first passphrase fails, no second specified - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, fmt.Errorf("bad passphrase")).Times(1) - - secrets = map[string]string{ - "luks-passphrase": "secretA", - "luks-passphrase-name": "A", - } - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.Error(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Test first passphrase fails, second is blank - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, fmt.Errorf("bad passphrase")).Times(1) - secrets = map[string]string{ - "luks-passphrase": "secretA", - "luks-passphrase-name": "A", - "previous-luks-passphrase": "", - "previous-luks-passphrase-name": "", - } - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.Error(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Test first passphrase fails, second is the same - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, fmt.Errorf("bad passphrase")).Times(1) - secrets = map[string]string{ - "luks-passphrase": "secretA", - "luks-passphrase-name": "A", - "previous-luks-passphrase": "secretA", - "previous-luks-passphrase-name": "A", - } - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.Error(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Test first passphrase is blank - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - secrets = map[string]string{ - "luks-passphrase": "", - "luks-passphrase-name": "", - "previous-luks-passphrase": "secretB", - "previous-luks-passphrase-name": "B", - } - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.Error(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Passphrase rotation fails - execReturnCode = 4 - secrets = map[string]string{ - "luks-passphrase": "secretB", - "luks-passphrase-name": "B", - "previous-luks-passphrase": "secretA", - "previous-luks-passphrase-name": "A", - } - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretB").Return(false, fmt.Errorf("bad passphrase")).Times(1) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, nil) - - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.NoError(t, err) - assert.False(t, luksFormatted) - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Test second passphrase is also incorrect - secrets = map[string]string{ - "luks-passphrase": "secretA", - "luks-passphrase-name": "A", - "previous-luks-passphrase": "secretB", - "previous-luks-passphrase-name": "B", - } - mockCtrl = gomock.NewController(t) - mockLUKSDevice = mock_luks.NewMockLUKSDeviceInterface(mockCtrl) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretA").Return(false, fmt.Errorf("bad passphrase")) - mockLUKSDevice.EXPECT().EnsureFormattedAndOpen(gomock.Any(), "secretB").Return(false, fmt.Errorf("bad passphrase")) - - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(context.Background(), mockLUKSDevice, "pvc-test", secrets) - assert.Error(t, err) - assert.False(t, luksFormatted) -} - -func TestRemoveMultipathDeviceMapping(t *testing.T) { - originalCmd := command - // Reset 'command' at the end of the test - defer func() { command = originalCmd }() - - mockCommand := mockexec.NewMockCommand(gomock.NewController(t)) - command = mockCommand // Set package var to mock - - tests := []struct { - name string - devicePath string - mockReturn []byte - mockError error - expectError bool - }{ - { - name: "Happy Path", - devicePath: "/dev/mock-0", - mockReturn: []byte("mock output"), - mockError: nil, - expectError: false, - }, - { - name: "Blank Device Path", - devicePath: "", - mockReturn: nil, - mockError: nil, - expectError: false, - }, - { - name: "Device does not exist", - devicePath: "/dev/mapper/doesNotExist", - mockReturn: []byte("'/dev/mapper/doesNotExist' is not a valid argument"), - mockError: fmt.Errorf("error"), - expectError: false, - }, - { - name: "Negative case", - devicePath: "/dev/mock-0", - mockReturn: nil, - mockError: fmt.Errorf("error"), - expectError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.devicePath != "" { - mockCommand.EXPECT().ExecuteWithTimeout(gomock.Any(), "multipath", 10*time.Second, false, "-f", tt.devicePath). - Return(tt.mockReturn, tt.mockError) - } - - err := RemoveMultipathDeviceMapping(context.TODO(), tt.devicePath) - if tt.expectError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestWaitForDevicesRemoval(t *testing.T) { - errMsg := "timed out waiting for devices to be removed" - tests := map[string]struct { - name string - devicePathPrefix string - deviceNames []string - getOsFs func() (afero.Fs, error) - maxWaitTime time.Duration - expectedError error - }{ - "Devices removed successfully": { - devicePathPrefix: "/dev", - deviceNames: []string{"sda", "sdb"}, - getOsFs: func() (afero.Fs, error) { - return afero.NewMemMapFs(), nil - }, - maxWaitTime: 1 * time.Second, - expectedError: nil, - }, - "Timeout waiting for devices to be removed": { - devicePathPrefix: "/dev", - deviceNames: []string{"sda", "sdb"}, - getOsFs: func() (afero.Fs, error) { - osFs := afero.NewMemMapFs() - _, err := osFs.Create("/dev/sda") - if err != nil { - return nil, err - } - _, err = osFs.Create("/dev/sdb") - if err != nil { - return nil, err - } - return osFs, nil - }, - maxWaitTime: 1 * time.Second, - expectedError: errors.TimeoutError(errMsg), - }, - "Timeout waiting for last device to be removed": { - devicePathPrefix: "/dev", - deviceNames: []string{"sda", "sdb"}, - getOsFs: func() (afero.Fs, error) { - osFs := afero.NewMemMapFs() - _, err := osFs.Create("/dev/sdb") - if err != nil { - return nil, err - } - return osFs, nil - }, - maxWaitTime: 1 * time.Second, - expectedError: errors.TimeoutError(errMsg), - }, - "Timeout waiting for first device to be removed": { - devicePathPrefix: "/dev", - deviceNames: []string{"sda", "sdb"}, - getOsFs: func() (afero.Fs, error) { - osFs := afero.NewMemMapFs() - _, err := osFs.Create("/dev/sda") - if err != nil { - return nil, err - } - return osFs, nil - }, - maxWaitTime: 1 * time.Second, - expectedError: errors.TimeoutError(errMsg), - }, - } - - for name, params := range tests { - t.Run(name, func(t *testing.T) { - fs, err := params.getOsFs() - assert.NoError(t, err) - err = waitForDevicesRemoval(context.Background(), fs, params.devicePathPrefix, params.deviceNames, - params.maxWaitTime) - if params.expectedError != nil { - assert.EqualError(t, err, params.expectedError.Error()) - } else { - assert.NoError(t, err) - } - }) - } -} diff --git a/utils/devices_windows.go b/utils/devices_windows.go deleted file mode 100644 index c5e7fd770..000000000 --- a/utils/devices_windows.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. - -// NOTE: This file should only contain functions for handling devices for windows flavor - -package utils - -import ( - "golang.org/x/net/context" - - . "github.com/netapp/trident/logging" - "github.com/netapp/trident/utils/errors" -) - -// flushOneDevice unused stub function -func flushOneDevice(ctx context.Context, devicePath string) error { - Logc(ctx).Debug(">>>> devices_windows.flushOneDevice") - defer Logc(ctx).Debug("<<<< devices_windows.flushOneDevice") - return errors.UnsupportedError("flushOneDevice is not supported for windows") -} - -// getISCSIDiskSize unused stub function -func getISCSIDiskSize(ctx context.Context, _ string) (int64, error) { - Logc(ctx).Debug(">>>> devices_windows.getISCSIDiskSize") - defer Logc(ctx).Debug("<<<< devices_windows.getISCSIDiskSize") - return 0, errors.UnsupportedError("getBlockSize is not supported for windows") -} - -func IsOpenLUKSDevice(ctx context.Context, devicePath string) (bool, error) { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - Logc(ctx).Debug(">>>> devices_windows.IsLUKSDeviceOpen") - defer Logc(ctx).Debug("<<<< devices_windows.IsLUKSDeviceOpen") - return false, errors.UnsupportedError("IsLUKSDeviceOpen is not supported for windows") -} - -func EnsureLUKSDeviceClosed(ctx context.Context, luksDevicePath string) error { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - Logc(ctx).Debug(">>>> devices_windows.EnsureLUKSDeviceClosed") - defer Logc(ctx).Debug("<<<< devices_windows.EnsureLUKSDeviceClosed") - return errors.UnsupportedError("EnsureLUKSDeviceClosed is not supported for windows") -} - -func getDeviceFSType(ctx context.Context, device string) (string, error) { - Logc(ctx).Debug(">>>> devices_windows.getDeviceFSType") - defer Logc(ctx).Debug("<<<< devices_windows.getDeviceFSType") - return "", errors.UnsupportedError("getDeviceFSTypeis not supported for windows") -} - -func getDeviceFSTypeRetry(ctx context.Context, device string) (string, error) { - Logc(ctx).Debug(">>>> devices_windows.getDeviceFSTypeRetry") - defer Logc(ctx).Debug("<<<< devices_windows.getDeviceFSTypeRetry") - return "", errors.UnsupportedError("getDeviceFSTypeRetry is not supported for windows") -} - -// IsLUKSFormatted returns whether LUKS headers have been placed on the device -func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { - Logc(ctx).Debug(">>>> devices_windows.IsLUKSFormatted") - defer Logc(ctx).Debug("<<<< devices_windows.IsLUKSFormatted") - return false, errors.UnsupportedError("IsLUKSFormatted is not supported for windows") -} - -// IsOpen returns whether the device is an active luks device on the host -func (d *LUKSDevice) IsOpen(ctx context.Context) (bool, error) { - Logc(ctx).Debug(">>>> devices_windows.IsOpen") - defer Logc(ctx).Debug("<<<< devices_windows.IsOpen") - return false, errors.UnsupportedError("IsOpen is not supported for windows") -} - -// LUKSFormat attempts to set up LUKS headers on a device with the specified passphrase, but bails if the -// underlying device already has a format present that is not LUKS. -func (d *LUKSDevice) LUKSFormat(ctx context.Context, _ string) error { - Logc(ctx).Debug(">>>> devices_windows.LUKSFormat") - defer Logc(ctx).Debug("<<<< devices_windows.LUKSFormat") - return errors.UnsupportedError("LUKSFormat is not supported for windows") -} - -// Open makes the device accessible on the host -func (d *LUKSDevice) Open(ctx context.Context, luksPassphrase string) error { - Logc(ctx).Debug(">>>> devices_windows.Open") - defer Logc(ctx).Debug("<<<< devices_windows.Open") - return errors.UnsupportedError("Open is not supported for windows") -} - -// Close performs a luksClose on the LUKS device -func (d *LUKSDevice) Close(ctx context.Context) error { - Logc(ctx).Debug(">>>> devices_windows.Close") - defer Logc(ctx).Debug("<<<< devices_windows.Close") - return errors.UnsupportedError("Close is not supported for windows") -} - -func (d *LUKSDevice) RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error { - Logc(ctx).Debug(">>>> devices_windows.RotatePassphrase") - defer Logc(ctx).Debug("<<<< devices_windows.RotatePassphrase") - return errors.UnsupportedError("RotatePassphrase is not supported for windows") -} - -// Resize performs a luksResize on the LUKS device -func (d *LUKSDevice) Resize(ctx context.Context, luksPassphrase string) error { - Logc(ctx).Debug(">>>> devices_darwin.Resize") - defer Logc(ctx).Debug("<<<< devices_darwin.Resize") - return errors.UnsupportedError("Resize is not supported for darwin") -} - -func (d *LUKSDevice) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) { - Logc(ctx).Debug(">>>> devices_windows.CheckPassphrase") - defer Logc(ctx).Debug("<<<< devices_windows.CheckPassphrase") - return false, errors.UnsupportedError("CheckPassphrase is not supported for windows") -} - -func GetUnderlyingDevicePathForLUKSDevice(ctx context.Context, luksDevicePath string) (string, error) { - Logc(ctx).Debug(">>>> devices_windows.GetUnderlyingDevicePathForLUKSDevice") - defer Logc(ctx).Debug("<<<< devices_windows.GetUnderlyingDevicePathForLUKSDevice") - return "", errors.UnsupportedError("GetUnderlyingDevicePathForLUKSDevice is not supported for windows") -} - -func EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { - Logc(ctx).Debug(">>>> devices_windows.EnsureLUKSDeviceClosedWithMaxWaitLimit") - defer Logc(ctx).Debug("<<<< devices_windows.EnsureLUKSDeviceClosedWithMaxWaitLimit") - return errors.UnsupportedError("EnsureLUKSDeviceClosedWithMaxWaitLimit is not supported for windows") -} - -func closeLUKSDevice(ctx context.Context, devicePath string) error { - Logc(ctx).Debug(">>>> devices_windows.closeLUKSDevice") - defer Logc(ctx).Debug("<<<< devices_windows.closeLUKSDevice") - return errors.UnsupportedError("closeLUKSDevice is not supported for windows") -} - -func getFCPDiskSize(ctx context.Context, devicePath string) (int64, error) { - Logc(ctx).Debug(">>>> devices_windows.getFCPDiskSize") - defer Logc(ctx).Debug("<<<< devices_windows.getFCPDiskSize") - return 0, errors.UnsupportedError("getFCPDiskSize is not supported for windows") -} diff --git a/utils/exec/command.go b/utils/exec/command.go index 2c3e3706d..5cf0e37c7 100644 --- a/utils/exec/command.go +++ b/utils/exec/command.go @@ -22,6 +22,13 @@ var ( _ Command = NewCommand() ) +// ExitErrorInterface defines the methods that exec.ExitError implements. +// This enables unit testing and mocking of exit codes. +type ExitError interface { + error + ExitCode() int +} + // Command defines a set of behaviors for executing commands on a host. type Command interface { Execute(ctx context.Context, name string, args ...string) ([]byte, error) diff --git a/utils/fcp.go b/utils/fcp.go index 0b062d95c..3f0927758 100644 --- a/utils/fcp.go +++ b/utils/fcp.go @@ -8,5 +8,5 @@ import ( var ( FcpUtils = fcp.NewReconcileUtils(chrootPathPrefix, NewOSClient()) fcpClient = fcp.NewDetailed(chrootPathPrefix, command, fcp.DefaultSelfHealingExclusion, NewOSClient(), - NewDevicesClient(), filesystem.New(mountClient), mountClient, FcpUtils) + devicesClient, filesystem.New(mountClient), mountClient, FcpUtils) ) diff --git a/utils/fcp/expose.go b/utils/fcp/expose.go deleted file mode 100644 index 1ee6e590a..000000000 --- a/utils/fcp/expose.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. - -package fcp - -import "context" - -// this is a temporary file that will be used to expose the internal functions of the iscsi package till we move -// all the iscsi ui related functions to the iscsi package -// TODO (vivintw) remove this file once the refactoring is done. - -func ListAllDevices(ctx context.Context) { - listAllDevices(ctx) -} - -func (client *Client) ScanTargetLUN(ctx context.Context, lunID int, hosts []int) error { - return client.scanTargetLUN(ctx, lunID, hosts) -} - -func (client *Client) GetDeviceInfoForLUN( - ctx context.Context, lunID int, iSCSINodeName string, needFSType, isDetachCall bool, -) (*ScsiDeviceInfo, error) { - return client.getDeviceInfoForLUN(ctx, lunID, iSCSINodeName, needFSType, isDetachCall) -} - -func (client *Client) FindMultipathDeviceForDevice(ctx context.Context, device string) string { - return client.findMultipathDeviceForDevice(ctx, device) -} - -func GetLunSerial(ctx context.Context, path string) (string, error) { - return getLunSerial(ctx, path) -} diff --git a/utils/fcp/fcp.go b/utils/fcp/fcp.go index 71b49ca64..e17569e8b 100644 --- a/utils/fcp/fcp.go +++ b/utils/fcp/fcp.go @@ -6,7 +6,6 @@ package fcp import ( "context" - "encoding/binary" "encoding/hex" "fmt" "os" @@ -17,18 +16,17 @@ import ( "github.com/cenkalti/backoff/v4" - "github.com/netapp/trident/utils/mount" - // "github.com/netapp/trident/internal/fiji" . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/devices" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" tridentexec "github.com/netapp/trident/utils/exec" "github.com/netapp/trident/utils/filesystem" "github.com/netapp/trident/utils/models" + "github.com/netapp/trident/utils/mount" ) const ( - unknownFstype = "" - DevPrefix = "/dev/" DevMapperRoot = "/dev/mapper/" @@ -55,6 +53,9 @@ type FCP interface { PreChecks(ctx context.Context) error RescanDevices(ctx context.Context, targetWWNN string, lunID int32, minSize int64) error IsAlreadyAttached(ctx context.Context, lunID int, targetWWNN string) bool + GetDeviceInfoForLUN( + ctx context.Context, lunID int, fcpNodeName string, needFSType, isDetachCall bool, + ) (*ScsiDeviceInfo, error) } // DefaultSelfHealingExclusion Exclusion list contains keywords if found in any Target WWNN should not be considered for @@ -68,14 +69,10 @@ type OS interface { type Devices interface { WaitForDevice(ctx context.Context, device string) error GetDeviceFSType(ctx context.Context, device string) (string, error) - NewLUKSDevice(rawDevicePath, volumeId string) (models.LUKSDeviceInterface, error) - EnsureLUKSDeviceMappedOnHost( - ctx context.Context, luksDevice models.LUKSDeviceInterface, name string, - secrets map[string]string, - ) (bool, error) + NewLUKSDevice(rawDevicePath, volumeId string) (luks.Device, error) + EnsureLUKSDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, error) IsDeviceUnformatted(ctx context.Context, device string) (bool, error) EnsureDeviceReadable(ctx context.Context, device string) error - GetFCPDiskSize(ctx context.Context, devicePath string) (int64, error) } type FileSystem interface { @@ -93,13 +90,13 @@ type Client struct { command tridentexec.Command selfHealingExclusion []string osClient OS - deviceClient Devices + deviceClient devices.Devices fs FileSystem mount Mount fcpUtils FcpReconcileUtils } -func New(osClient OS, deviceClient Devices, fileSystemClient FileSystem) (*Client, error) { +func New(osClient OS, fileSystemClient FileSystem) (*Client, error) { chrootPathPrefix := "" if os.Getenv("DOCKER_PLUGIN_MODE") != "" { chrootPathPrefix = "/host" @@ -113,12 +110,12 @@ func New(osClient OS, deviceClient Devices, fileSystemClient FileSystem) (*Clien } return NewDetailed(chrootPathPrefix, tridentexec.NewCommand(), DefaultSelfHealingExclusion, osClient, - deviceClient, fileSystemClient, mountClient, reconcileutils), nil + devices.New(), fileSystemClient, mountClient, reconcileutils), nil } func NewDetailed( chrootPathPrefix string, command tridentexec.Command, selfHealingExclusion []string, - osClient OS, deviceClient Devices, fs FileSystem, mount Mount, + osClient OS, deviceClient devices.Devices, fs FileSystem, mount Mount, fcpUtils FcpReconcileUtils, ) *Client { return &Client{ @@ -176,7 +173,7 @@ func (client *Client) RescanDevices(ctx context.Context, targetWWNN string, lunI Logc(ctx).WithFields(fields).Debug(">>>> fcp.RescanDevices") defer Logc(ctx).WithFields(fields).Debug("<<<< fcp.RescanDevices") - deviceInfo, err := client.getDeviceInfoForLUN(ctx, int(lunID), targetWWNN, false, false) + deviceInfo, err := client.GetDeviceInfoForLUN(ctx, int(lunID), targetWWNN, false, false) if err != nil { return fmt.Errorf("error getting FCP device information: %s", err) } else if deviceInfo == nil { @@ -185,7 +182,7 @@ func (client *Client) RescanDevices(ctx context.Context, targetWWNN string, lunI allLargeEnough := true for _, diskDevice := range deviceInfo.Devices { - size, err := client.deviceClient.GetFCPDiskSize(ctx, DevPrefix+diskDevice) + size, err := client.deviceClient.GetDiskSize(ctx, DevPrefix+diskDevice) if err != nil { return err } @@ -205,7 +202,7 @@ func (client *Client) RescanDevices(ctx context.Context, targetWWNN string, lunI if !allLargeEnough { time.Sleep(time.Second) for _, diskDevice := range deviceInfo.Devices { - size, err := client.deviceClient.GetFCPDiskSize(ctx, DevPrefix+diskDevice) + size, err := client.deviceClient.GetDiskSize(ctx, DevPrefix+diskDevice) if err != nil { return err } @@ -218,7 +215,7 @@ func (client *Client) RescanDevices(ctx context.Context, targetWWNN string, lunI if deviceInfo.MultipathDevice != "" { multipathDevice := deviceInfo.MultipathDevice - size, err := client.deviceClient.GetFCPDiskSize(ctx, DevPrefix+multipathDevice) + size, err := client.deviceClient.GetDiskSize(ctx, DevPrefix+multipathDevice) if err != nil { return err } @@ -232,7 +229,7 @@ func (client *Client) RescanDevices(ctx context.Context, targetWWNN string, lunI } // TODO (vhs): Introduce a backoff rather than a fixed delay. time.Sleep(time.Second) - size, err = client.deviceClient.GetFCPDiskSize(ctx, DevPrefix+multipathDevice) + size, err = client.deviceClient.GetDiskSize(ctx, DevPrefix+multipathDevice) if err != nil { return err } @@ -255,7 +252,7 @@ func (client *Client) rescanDisk(ctx context.Context, deviceName string) error { Logc(ctx).WithFields(fields).Debug(">>>> fcp.rescanDisk") defer Logc(ctx).WithFields(fields).Debug("<<<< fcp.rescanDisk") - listAllDevices(ctx) + client.deviceClient.ListAllDevices(ctx) filename := fmt.Sprintf(client.chrootPathPrefix+"/sys/block/%s/device/rescan", deviceName) Logc(ctx).WithField("filename", filename).Debug("Opening file for writing.") @@ -278,7 +275,7 @@ func (client *Client) rescanDisk(ctx context.Context, deviceName string) error { return fmt.Errorf("no data written to %s", filename) } - listAllDevices(ctx) + client.deviceClient.ListAllDevices(ctx) return nil } @@ -391,7 +388,7 @@ func (client *Client) AttachVolume( } // Lookup all the SCSI device information - deviceInfo, err := client.getDeviceInfoForLUN(ctx, lunID, publishInfo.FCTargetWWNN, false, false) + deviceInfo, err := client.GetDeviceInfoForLUN(ctx, lunID, publishInfo.FCTargetWWNN, false, false) if err != nil { return mpathSize, fmt.Errorf("error getting FCP device information: %v", err) } else if deviceInfo == nil { @@ -430,7 +427,8 @@ func (client *Client) AttachVolume( // Once the multipath device has been found, compare its size with // the size of one of the devices, if it differs then mark it for // resize after the staging. - correctMpathSize, mpathSizeCorrect, err := client.verifyMultipathDeviceSize(ctx, deviceToUse, deviceInfo.Devices[0]) + correctMpathSize, mpathSizeCorrect, err := client.deviceClient.VerifyMultipathDeviceSize(ctx, deviceToUse, + deviceInfo.Devices[0]) if err != nil { Logc(ctx).WithFields(LogFields{ "scsiLun": deviceInfo.LUN, @@ -476,8 +474,8 @@ func (client *Client) AttachVolume( } if isLUKSDevice { - luksDevice, _ := client.deviceClient.NewLUKSDevice(devicePath, name) - luksFormatted, err = client.deviceClient.EnsureLUKSDeviceMappedOnHost(ctx, luksDevice, name, secrets) + luksDevice := luks.NewLUKSDevice(devicePath, name, client.command) + luksFormatted, err = luksDevice.EnsureLUKSDeviceMappedOnHost(ctx, name, secrets) if err != nil { return mpathSize, err } @@ -519,7 +517,7 @@ func (client *Client) AttachVolume( if err != nil { return mpathSize, fmt.Errorf("error formatting LUN %s, device %s: %v", name, deviceToUse, err) } - } else if existingFstype != unknownFstype && existingFstype != publishInfo.FilesystemType { + } else if existingFstype != filesystem.UnknownFstype && existingFstype != publishInfo.FilesystemType { Logc(ctx).WithFields(LogFields{ "volume": name, "existingFstype": existingFstype, @@ -573,9 +571,9 @@ type ScsiDeviceInfo struct { HostSessionMap map[int]int } -// getDeviceInfoForLUN finds FCP devices using /dev/disk/by-path values. This method should be +// GetDeviceInfoForLUN finds FCP devices using /dev/disk/by-path values. This method should be // called after calling waitForDeviceScan so that the device paths are known to exist. -func (client *Client) getDeviceInfoForLUN( +func (client *Client) GetDeviceInfoForLUN( ctx context.Context, lunID int, fcpNodeName string, needFSType, isDetachCall bool, ) (*ScsiDeviceInfo, error) { fields := LogFields{ @@ -609,7 +607,7 @@ func (client *Client) getDeviceInfoForLUN( multipathDevice := "" for _, device := range devices { - multipathDevice = client.findMultipathDeviceForDevice(ctx, device) + multipathDevice = client.deviceClient.FindMultipathDeviceForDevice(ctx, device) if multipathDevice != "" { break } @@ -749,7 +747,7 @@ func (client *Client) waitForMultipathDeviceForDevices(ctx context.Context, devi multipathDevice := "" for _, device := range devices { - multipathDevice = client.findMultipathDeviceForDevice(ctx, device) + multipathDevice = client.deviceClient.FindMultipathDeviceForDevice(ctx, device) if multipathDevice != "" { break } @@ -766,25 +764,6 @@ func (client *Client) waitForMultipathDeviceForDevices(ctx context.Context, devi return multipathDevice, nil } -// findMultipathDeviceForDevice finds the devicemapper parent of a device name like /dev/sdx. -func (client *Client) findMultipathDeviceForDevice(ctx context.Context, device string) string { - Logc(ctx).WithField("device", device).Debug(">>>> fcp.findMultipathDeviceForDevice") - defer Logc(ctx).WithField("device", device).Debug("<<<< fcp.findMultipathDeviceForDevice") - - holdersDir := client.chrootPathPrefix + "/sys/block/" + device + "/holders" - if dirs, err := os.ReadDir(holdersDir); err == nil { - for _, f := range dirs { - name := f.Name() - if strings.HasPrefix(name, "dm-") { - return name - } - } - } - - Logc(ctx).WithField("device", device).Debug("Could not find multipath device for device.") - return "" -} - // waitForDeviceScan scans all paths to a specific LUN and waits until all // SCSI disk-by-path devices for that LUN are present on the host. func (client *Client) waitForDeviceScan(ctx context.Context, lunID int, fcpNodeName string) error { @@ -818,7 +797,7 @@ func (client *Client) waitForDeviceScan(ctx context.Context, lunID int, fcpNodeN } } - if err := client.ScanTargetLUN(ctx, lunID, hosts); err != nil { + if err := client.deviceClient.ScanTargetLUN(ctx, lunID, hosts); err != nil { Logc(ctx).WithField("scanError", err).Error("Could not scan for new LUN.") } @@ -879,60 +858,6 @@ func (client *Client) waitForDeviceScan(ctx context.Context, lunID int, fcpNodeN return nil } -// scanTargetLUN scans a single LUN or all the LUNs on an FCP target to discover it. -// If all the LUNs are to be scanned please pass -1 for lunID. -func (client *Client) scanTargetLUN(ctx context.Context, lunID int, hosts []int) error { - fields := LogFields{"hosts": hosts, "lunID": lunID} - Logc(ctx).WithFields(fields).Debug(">>>> fcp.scanTargetLUN") - defer Logc(ctx).WithFields(fields).Debug("<<<< fcp.scanTargetLUN") - - var ( - f *os.File - err error - ) - - // By default, scan for all the LUNs - scanCmd := "0 0 -" - if lunID >= 0 { - scanCmd = fmt.Sprintf("0 0 %d", lunID) - } - - listAllDevices(ctx) - for _, hostNumber := range hosts { - - filename := fmt.Sprintf(client.chrootPathPrefix+"/sys/class/scsi_host/host%d/scan", hostNumber) - if f, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0o200); err != nil { - Logc(ctx).WithField("file", filename).Warning("Could not open file for writing.") - return err - } - - // if err = duringScanTargetLunAfterFileOpen.Inject(); err != nil { - // return err - // } - - if written, err := f.WriteString(scanCmd); err != nil { - Logc(ctx).WithFields(LogFields{"file": filename, "error": err}).Warning("Could not write to file.") - f.Close() - return err - } else if written == 0 { - Logc(ctx).WithField("file", filename).Warning("No data written to file.") - f.Close() - return fmt.Errorf("no data written to %s", filename) - } - - f.Close() - - listAllDevices(ctx) - Logc(ctx).WithFields(LogFields{ - "scanCmd": scanCmd, - "scanFile": filename, - "host": hostNumber, - }).Debug("Invoked SCSI scan for host.") - } - - return nil -} - // handleInvalidSerials checks the LUN serial number for each path of a given LUN, and // if it doesn't match the expected value, runs a handler function. func (client *Client) handleInvalidSerials( @@ -947,7 +872,7 @@ func (client *Client) handleInvalidSerials( hostSessionMap := client.fcpUtils.GetFCPHostSessionMapForTarget(ctx, targetWWNN) paths := client.fcpUtils.GetSysfsBlockDirsForLUN(lunID, hostSessionMap) for _, path := range paths { - serial, err := getLunSerial(ctx, path) + serial, err := client.deviceClient.GetLunSerial(ctx, path) if err != nil { if os.IsNotExist(err) { // LUN either isn't scanned yet, or this kernel @@ -988,35 +913,6 @@ func (client *Client) handleInvalidSerials( return nil } -// getLunSerial get Linux's idea of what the LUN serial number is -func getLunSerial(ctx context.Context, path string) (string, error) { - Logc(ctx).WithField("path", path).Debug("Get LUN Serial") - // We're going to read the SCSI VPD page 80 serial number - // information. Linux helpfully provides this through sysfs - // so we don't need to open the device and send the ioctl - // ourselves. - filename := path + "/vpd_pg80" - b, err := os.ReadFile(filename) - if err != nil { - return "", err - } - if 4 > len(b) || 0x80 != b[1] { - Logc(ctx).WithFields(LogFields{ - "data": b, - }).Error("VPD page 80 format check failed") - return "", fmt.Errorf("malformed VPD page 80 data") - } - length := int(binary.BigEndian.Uint16(b[2:4])) - if len(b) != length+4 { - Logc(ctx).WithFields(LogFields{ - "actual": len(b), - "expected": length + 4, - }).Error("VPD page 80 length check failed") - return "", fmt.Errorf("incorrect length for VPD page 80 serial number") - } - return string(b[4:]), nil -} - // verifyMultipathDeviceSerial compares the serial number of the DM device with the serial // of the LUN to ensure correct DM device has been discovered func (client *Client) verifyMultipathDeviceSerial( @@ -1030,7 +926,7 @@ func (client *Client) verifyMultipathDeviceSerial( // Multipath UUID contains LUN serial in hex format lunSerialHex := hex.EncodeToString([]byte(lunSerial)) - multipathDeviceUUID, err := client.fcpUtils.GetMultipathDeviceUUID(multipathDevice) + multipathDeviceUUID, err := client.deviceClient.GetMultipathDeviceUUID(multipathDevice) if err != nil { if errors.IsNotFoundError(err) { // If UUID does not exist, then it is hard to verify the DM serial @@ -1072,63 +968,6 @@ func (client *Client) verifyMultipathDeviceSerial( return nil } -// verifyMultipathDeviceSize compares the size of the DM device with the size -// of a device to ensure correct DM device has the correct size. -func (client *Client) verifyMultipathDeviceSize( - ctx context.Context, multipathDevice, device string, -) (int64, bool, error) { - deviceSize, err := client.deviceClient.GetFCPDiskSize(ctx, "/dev/"+device) - if err != nil { - return 0, false, err - } - - mpathSize, err := client.deviceClient.GetFCPDiskSize(ctx, "/dev/"+multipathDevice) - if err != nil { - return 0, false, err - } - - if deviceSize != mpathSize { - return deviceSize, false, nil - } - - Logc(ctx).WithFields(LogFields{ - "multipathDevice": multipathDevice, - "device": device, - }).Debug("Multipath device size check passed.") - - return 0, true, nil -} - -// In the case of FCP trace debug, log info about session and what devices are present -func listAllDevices(ctx context.Context) { - Logc(ctx).Trace(">>>> fcp.listAllDevices") - defer Logc(ctx).Trace("<<<< fcp.listAllDevices") - // Log information about all the devices - dmLog := make([]string, 0) - sdLog := make([]string, 0) - sysLog := make([]string, 0) - entries, _ := os.ReadDir(DevPrefix) - for _, entry := range entries { - if strings.HasPrefix(entry.Name(), "dm-") { - dmLog = append(dmLog, entry.Name()) - } - if strings.HasPrefix(entry.Name(), "sd") { - sdLog = append(sdLog, entry.Name()) - } - } - - entries, _ = os.ReadDir("/sys/block/") - for _, entry := range entries { - sysLog = append(sysLog, entry.Name()) - } - - Logc(ctx).WithFields(LogFields{ - "/dev/dm-*": dmLog, - "/dev/sd*": sdLog, - "/sys/block/*": sysLog, - }).Trace("Listing all FCP Devices.") -} - // PreChecks to check if all the required tools are present and configured correctly for the volume // attachment to go through func (client *Client) PreChecks(ctx context.Context) error { diff --git a/utils/fcp/reconcile_utils.go b/utils/fcp/reconcile_utils.go index e93a7c3c2..d384366fd 100644 --- a/utils/fcp/reconcile_utils.go +++ b/utils/fcp/reconcile_utils.go @@ -13,16 +13,12 @@ import ( "strings" . "github.com/netapp/trident/logging" - "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/models" ) type FcpReconcileUtils interface { GetFCPHostSessionMapForTarget(context.Context, string) []map[string]int GetSysfsBlockDirsForLUN(int, []map[string]int) []string - GetMultipathDeviceUUID(string) (string, error) - GetMultipathDeviceBySerial(context.Context, string) (string, error) - GetMultipathDeviceDisks(context.Context, string) ([]string, error) GetDevicesForLUN(paths []string) ([]string, error) ReconcileFCPVolumeInfo(ctx context.Context, trackingInfo *models.VolumeTrackingInfo) (bool, error) } @@ -39,89 +35,6 @@ func NewReconcileUtils(chrootPathPrefix string, osClient OS) FcpReconcileUtils { } } -// GetMultipathDeviceUUID find the /sys/block/dmX/dm/uuid UUID that contains DM device serial in hex format. -func (h *FcpReconcileHelper) GetMultipathDeviceUUID(multipathDevicePath string) (string, error) { - multipathDevice := strings.TrimPrefix(multipathDevicePath, "/dev/") - - deviceUUIDPath := h.chrootPathPrefix + fmt.Sprintf("/sys/block/%s/dm/uuid", multipathDevice) - - exists, err := h.osClient.PathExists(deviceUUIDPath) - if !exists || err != nil { - return "", errors.NotFoundError("multipath device '%s' UUID not found", multipathDevice) - } - - UUID, err := os.ReadFile(deviceUUIDPath) - if err != nil { - return "", err - } - - return string(UUID), nil -} - -// GetMultipathDeviceDisks find the /sys/block/dmX/slaves/sdX disks. -func (h *FcpReconcileHelper) GetMultipathDeviceDisks( - ctx context.Context, multipathDevicePath string, -) ([]string, error) { - devices := make([]string, 0) - multipathDevice := strings.TrimPrefix(multipathDevicePath, "/dev/") - - diskPath := h.chrootPathPrefix + fmt.Sprintf("/sys/block/%s/slaves/", multipathDevice) - diskDirs, err := os.ReadDir(diskPath) - if err != nil { - Logc(ctx).WithError(err).Errorf("Could not read %s", diskDirs) - return nil, fmt.Errorf("failed to identify multipath device disks; unable to read '%s'", diskDirs) - } - - for _, diskDir := range diskDirs { - contentName := diskDir.Name() - if !strings.HasPrefix(contentName, "sd") { - continue - } - - devices = append(devices, contentName) - } - - return devices, nil -} - -// GetMultipathDeviceBySerial find DM device whose UUID /sys/block/dmX/dm/uuid contains serial in hex format. -func (h *FcpReconcileHelper) GetMultipathDeviceBySerial(ctx context.Context, hexSerial string) (string, error) { - sysPath := h.chrootPathPrefix + "/sys/block/" - - blockDirs, err := os.ReadDir(sysPath) - if err != nil { - Logc(ctx).WithError(err).Errorf("Could not read %s", sysPath) - return "", fmt.Errorf("failed to find multipath device by serial; unable to read '%s'", sysPath) - } - - for _, blockDir := range blockDirs { - dmDeviceName := blockDir.Name() - if !strings.HasPrefix(dmDeviceName, "dm-") { - continue - } - - uuid, err := h.GetMultipathDeviceUUID(dmDeviceName) - if err != nil { - Logc(ctx).WithFields(LogFields{ - "UUID": hexSerial, - "multipathDevice": dmDeviceName, - "err": err, - }).Error("Failed to get UUID of multipath device.") - continue - } - - if strings.Contains(uuid, hexSerial) { - Logc(ctx).WithFields(LogFields{ - "UUID": hexSerial, - "multipathDevice": dmDeviceName, - }).Debug("Found multipath device by UUID.") - return dmDeviceName, nil - } - } - - return "", errors.NotFoundError("no multipath device found") -} - // ReconcileFCPVolumeInfo returns true if any of the expected conditions for a present volume are true (e.g. the // expected LUN exists). func (h *FcpReconcileHelper) ReconcileFCPVolumeInfo( diff --git a/utils/filesystem/filesystem.go b/utils/filesystem/filesystem.go index 2d00b6e5e..1757156d6 100644 --- a/utils/filesystem/filesystem.go +++ b/utils/filesystem/filesystem.go @@ -25,10 +25,11 @@ import ( const ( // Filesystem types - Xfs = "xfs" - Ext3 = "ext3" - Ext4 = "ext4" - Raw = "raw" + Xfs = "xfs" + Ext3 = "ext3" + Ext4 = "ext4" + Raw = "raw" + UnknownFstype = "" ) const ( @@ -64,7 +65,6 @@ type Filesystem interface { ctx context.Context, path string, ) (available, capacity, usage, inodes, inodesFree, inodesUsed int64, err error) GetUnmountPath(ctx context.Context, trackingInfo *models.VolumeTrackingInfo) (string, error) - GenerateAnonymousMemFile(tempFileName, content string) (int, error) } type Mount interface { diff --git a/utils/filesystem/filesystem_darwin.go b/utils/filesystem/filesystem_darwin.go index 9b2c38612..23907d78e 100644 --- a/utils/filesystem/filesystem_darwin.go +++ b/utils/filesystem/filesystem_darwin.go @@ -41,11 +41,3 @@ func (f *FSClient) GetUnmountPath(ctx context.Context, trackingInfo *models.Volu return "", errors.UnsupportedError("GetUnmountPath is not supported for darwin") } - -func (f *FSClient) GenerateAnonymousMemFile(tempFileName, content string) (int, error) { - ctx := context.Background() - Logc(ctx).Debug(">>>> filesystem_darwin.generateAnonymousMemFile") - defer Logc(ctx).Debug("<<<< filesystem_darwin.generateAnonymousMemFile") - - return 0, errors.UnsupportedError("generateAnonymousMemFile is not supported for darwin") -} diff --git a/utils/filesystem/filesystem_darwin_test.go b/utils/filesystem/filesystem_darwin_test.go index 5fc15257e..5b095008c 100644 --- a/utils/filesystem/filesystem_darwin_test.go +++ b/utils/filesystem/filesystem_darwin_test.go @@ -46,11 +46,3 @@ func TestGetUnmountPath(t *testing.T) { assert.Error(t, err, "no error") assert.True(t, errors.IsUnsupportedError(err), "not UnsupportedError") } - -func TestGenerateAnonymousMemFile(t *testing.T) { - fsClient := New(nil) - result, err := fsClient.GenerateAnonymousMemFile("", "") - assert.Equal(t, 0, result) - assert.Error(t, err, "no error") - assert.True(t, errors.IsUnsupportedError(err), "not UnsupportedError") -} diff --git a/utils/filesystem/filesystem_linux.go b/utils/filesystem/filesystem_linux.go index 02cdfbbfc..012671ad6 100644 --- a/utils/filesystem/filesystem_linux.go +++ b/utils/filesystem/filesystem_linux.go @@ -109,22 +109,3 @@ func (f *FSClient) GetUnmountPath(ctx context.Context, trackingInfo *models.Volu return "", errors.UnsupportedError("GetUnmountPath is not supported for linux") } - -// GenerateAnonymousMemFile uses linux syscall memfd_create to create an anonymous, temporary, in-memory file -// with the specified name and contents -func (f *FSClient) GenerateAnonymousMemFile(tempFileName, content string) (int, error) { - fd, err := unix.MemfdCreate(tempFileName, 0) - if err != nil { - return -1, fmt.Errorf("failed to create anonymous file; %v", err) - } - _, err = unix.Write(fd, []byte(content)) - if err != nil { - return fd, fmt.Errorf("failed to write anonymous file; %v", err) - } - // Rewind back to the beginning - _, err = unix.Seek(fd, 0, 0) - if err != nil { - return fd, fmt.Errorf("failed to rewind anonymous file; %v", err) - } - return fd, nil -} diff --git a/utils/filesystem/filesystem_linux_test.go b/utils/filesystem/filesystem_linux_test.go index 36470b328..1485a8587 100644 --- a/utils/filesystem/filesystem_linux_test.go +++ b/utils/filesystem/filesystem_linux_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" - "golang.org/x/sys/unix" "github.com/netapp/trident/mocks/mock_utils/mock_exec" "github.com/netapp/trident/mocks/mock_utils/mock_filesystem" @@ -28,26 +27,6 @@ func TestGetUnmountPath(t *testing.T) { assert.True(t, errors.IsUnsupportedError(err), "not UnsupportedError") } -func TestGenerateAnonymousMemFile(t *testing.T) { - fsClient := &FSClient{} - tempFileName := "testFile" - content := "testContent" - - fd, err := fsClient.GenerateAnonymousMemFile(tempFileName, content) - assert.NoError(t, err, "expected no error creating anonymous mem file") - assert.Greater(t, fd, 0, "expected valid file descriptor") - - // Read back the content to verify - readContent := make([]byte, len(content)) - _, err = unix.Read(fd, readContent) - assert.NoError(t, err, "expected no error reading anonymous mem file") - assert.Equal(t, content, string(readContent), "expected content to match") - - // Close the file descriptor - err = unix.Close(fd) - assert.NoError(t, err, "expected no error closing anonymous mem file") -} - func TestExpandFilesystemOnNode(t *testing.T) { mockVolumeInfo := &models.VolumePublishInfo{ DevicePath: "/mock/path", diff --git a/utils/filesystem/filesystem_windows.go b/utils/filesystem/filesystem_windows.go index 1107cd482..7a0358cb1 100644 --- a/utils/filesystem/filesystem_windows.go +++ b/utils/filesystem/filesystem_windows.go @@ -62,11 +62,3 @@ func (f *FSClient) GetUnmountPath(ctx context.Context, trackingInfo *models.Volu path := "\\" + trackingInfo.SMBServer + trackingInfo.SMBPath return strings.Replace(path, "/", "\\", -1), nil } - -func (f *FSClient) GenerateAnonymousMemFile(tempFileName, content string) (int, error) { - ctx := context.Background() - Logc(ctx).Debug(">>>> filesystem_windows.generateAnonymousMemFile") - defer Logc(ctx).Debug("<<<< filesystem_windows.generateAnonymousMemFile") - - return 0, errors.UnsupportedError("generateAnonymousMemFile is not supported for windows") -} diff --git a/utils/filesystem/filesystem_windows_test.go b/utils/filesystem/filesystem_windows_test.go index 9b66e5a21..b225bddaf 100644 --- a/utils/filesystem/filesystem_windows_test.go +++ b/utils/filesystem/filesystem_windows_test.go @@ -20,12 +20,3 @@ func TestGetFilesystemSize(t *testing.T) { assert.Error(t, err, "no error") assert.True(t, errors.IsUnsupportedError(err), "not UnsupportedError") } - -func TestGenerateAnonymousMemFile(t *testing.T) { - fsClient := New(nil) - - result, err := fsClient.GenerateAnonymousMemFile("", "") - assert.Equal(t, 0, result) - assert.Error(t, err, "no error") - assert.True(t, errors.IsUnsupportedError(err), "not UnsupportedError") -} diff --git a/utils/iscsi.go b/utils/iscsi.go index 82a3e3b66..c29e4ead5 100644 --- a/utils/iscsi.go +++ b/utils/iscsi.go @@ -19,6 +19,7 @@ import ( "github.com/netapp/trident/config" "github.com/netapp/trident/internal/fiji" . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/devices" "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/filesystem" "github.com/netapp/trident/utils/iscsi" @@ -27,20 +28,17 @@ import ( ) const ( - iSCSIMaxFlushWaitDuration = 6 * time.Minute - luksCloseMaxWaitDuration = 2 * time.Minute SessionSourceNodeStage = "nodeStage" SessionSourceTrackingInfo = "trackingInfo" SessionSourceCurrentStatus = "currentStatus" - - unknownFstype = "" ) var ( mountClient, _ = mount.New() IscsiUtils = iscsi.NewReconcileUtils(chrootPathPrefix, NewOSClient()) + devicesClient = devices.New() iscsiClient = iscsi.NewDetailed(chrootPathPrefix, command, iscsi.DefaultSelfHealingExclusion, NewOSClient(), - NewDevicesClient(), filesystem.New(mountClient), mountClient, IscsiUtils, afero.Afero{Fs: afero.NewOsFs()}) + devicesClient, filesystem.New(mountClient), mountClient, IscsiUtils, afero.Afero{Fs: afero.NewOsFs()}) // Non-persistent map to maintain flush delays/errors if any, for device path(s). iSCSIVolumeFlushExceptions = make(map[string]time.Time) @@ -228,7 +226,7 @@ func handleInvalidSerials( hostSessionMap := IscsiUtils.GetISCSIHostSessionMapForTarget(ctx, targetIqn) paths := IscsiUtils.GetSysfsBlockDirsForLUN(lunID, hostSessionMap) for _, path := range paths { - serial, err := getLunSerial(ctx, path) + serial, err := devicesClient.GetLunSerial(ctx, path) if err != nil { if os.IsNotExist(err) { // LUN either isn't scanned yet, or this kernel @@ -282,7 +280,7 @@ func verifyMultipathDeviceSerial( // Multipath UUID contains LUN serial in hex format lunSerialHex := hex.EncodeToString([]byte(lunSerial)) - multipathDeviceUUID, err := IscsiUtils.GetMultipathDeviceUUID(multipathDevice) + multipathDeviceUUID, err := devicesClient.GetMultipathDeviceUUID(multipathDevice) if err != nil { if errors.IsNotFoundError(err) { // If UUID does not exist, then it is hard to verify the DM serial @@ -324,33 +322,6 @@ func verifyMultipathDeviceSerial( return nil } -// verifyMultipathDeviceSize compares the size of the DM device with the size -// of a device to ensure correct DM device has the correct size. -func verifyMultipathDeviceSize( - ctx context.Context, multipathDevice, device string, -) (int64, bool, error) { - deviceSize, err := getISCSIDiskSize(ctx, "/dev/"+device) - if err != nil { - return 0, false, err - } - - mpathSize, err := getISCSIDiskSize(ctx, "/dev/"+multipathDevice) - if err != nil { - return 0, false, err - } - - if deviceSize != mpathSize { - return deviceSize, false, nil - } - - Logc(ctx).WithFields(LogFields{ - "multipathDevice": multipathDevice, - "device": device, - }).Debug("Multipath device size check passed.") - - return 0, true, nil -} - // filterTargets parses the output of iscsiadm -m node or -m discoverydb -t st -D // and returns the target IQNs for a given portal func filterTargets(output, tp string) []string { @@ -576,7 +547,7 @@ func PopulateCurrentSessions(ctx context.Context, currentMapping *models.ISCSISe } // Get all known iSCSI devices - iscsiDevices, err := GetISCSIDevices(ctx, true) + iscsiDevices, err := iscsiClient.GetISCSIDevices(ctx, true) if err != nil { Logc(ctx).WithField("error", err).Error("Failed to get list of iSCSI devices.") return err @@ -890,10 +861,6 @@ func execIscsiadmCommand(ctx context.Context, args ...string) ([]byte, error) { return iscsiClient.ExecIscsiadmCommand(ctx, args...) } -func listAllISCSIDevices(ctx context.Context) { - iscsiClient.ListAllDevices(ctx) -} - func getISCSISessionInfo(ctx context.Context) ([]iscsi.SessionInfo, error) { return iscsiClient.GetSessionInfo(ctx) } @@ -915,7 +882,7 @@ func LoginISCSITarget(ctx context.Context, publishInfo *models.VolumePublishInfo } func iSCSIScanTargetLUN(ctx context.Context, lunID int, hosts []int) error { - return iscsiClient.ScanTargetLUN(ctx, lunID, hosts) + return devicesClient.ScanTargetLUN(ctx, lunID, hosts) } func IsISCSISessionStale(ctx context.Context, sessionNumber string) bool { diff --git a/utils/iscsi/expose.go b/utils/iscsi/expose.go index 2c46ccff9..c3118373c 100644 --- a/utils/iscsi/expose.go +++ b/utils/iscsi/expose.go @@ -4,8 +4,6 @@ package iscsi import ( "context" - - "github.com/netapp/trident/utils/models" ) // this is a temporary file that will be used to expose the internal functions of the iscsi package till we move @@ -16,10 +14,6 @@ func (client *Client) ExecIscsiadmCommand(ctx context.Context, args ...string) ( return client.execIscsiadmCommand(ctx, args...) } -func (client *Client) ListAllDevices(ctx context.Context) { - client.listAllDevices(ctx) -} - func (client *Client) GetSessionInfo(ctx context.Context) ([]SessionInfo, error) { return client.getSessionInfo(ctx) } @@ -36,24 +30,6 @@ func (client *Client) ConfigureTarget(ctx context.Context, iqn, portal, name, va return client.configureTarget(ctx, iqn, portal, name, value) } -func (client *Client) ScanTargetLUN(ctx context.Context, lunID int, hosts []int) error { - return client.scanTargetLUN(ctx, lunID, hosts) -} - -func (client *Client) GetDeviceInfoForLUN( - ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, needFSType bool, -) (*models.ScsiDeviceInfo, error) { - return client.getDeviceInfoForLUN(ctx, hostSessionMap, lunID, iSCSINodeName, needFSType) -} - -func (client *Client) FindMultipathDeviceForDevice(ctx context.Context, device string) string { - return client.findMultipathDeviceForDevice(ctx, device) -} - -func (client *Client) GetLunSerial(ctx context.Context, path string) (string, error) { - return client.getLunSerial(ctx, path) -} - func (client *Client) IsSessionStale(ctx context.Context, sessionID string) bool { return client.isSessionStale(ctx, sessionID) } diff --git a/utils/iscsi/iscsi.go b/utils/iscsi/iscsi.go index c55b5b6be..222d16134 100644 --- a/utils/iscsi/iscsi.go +++ b/utils/iscsi/iscsi.go @@ -4,15 +4,14 @@ package iscsi //go:generate mockgen -destination=../../mocks/mock_utils/mock_iscsi/mock_iscsi_client.go github.com/netapp/trident/utils/iscsi ISCSI //go:generate mockgen -destination=../../mocks/mock_utils/mock_iscsi/mock_iscsi_os_client.go github.com/netapp/trident/utils/iscsi OS -//go:generate mockgen -destination=../../mocks/mock_utils/mock_iscsi/mock_iscsi_devices_client.go github.com/netapp/trident/utils/iscsi Devices import ( "context" - "encoding/binary" "encoding/hex" "fmt" "os" "os/exec" + "path/filepath" "regexp" "strconv" "strings" @@ -24,6 +23,8 @@ import ( "github.com/netapp/trident/internal/fiji" . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/devices" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" tridentexec "github.com/netapp/trident/utils/exec" "github.com/netapp/trident/utils/filesystem" @@ -32,10 +33,7 @@ import ( ) const ( - unknownFstype = "" - - DevPrefix = "/dev/" - DevMapperRoot = "/dev/mapper/" + DevPrefix = "/dev/" sessionStateLoggedIn = "LOGGED_IN" SessionInfoSource = "sessionSource" @@ -52,6 +50,8 @@ const ( REDACTED = "" temporaryMountDir = "/tmp_mnt" + + devicesRemovalMaxWaitTime = 5 * time.Second ) var ( @@ -59,7 +59,7 @@ var ( pidRegex = regexp.MustCompile(`^\d+$`) pidRunningOrIdleRegex = regexp.MustCompile(`pid \d+ (running|idle)`) - duringScanTargetLunAfterFileOpen = fiji.Register("duringISCSIScanTargetLunAfterFileOpen", "iscsi") + beforeFlushDevice = fiji.Register("beforeFlushDevice", "devices") duringConfigureISCSITargetBeforeISCSIAdmUpdate = fiji.Register("duringConfigureISCSITargetBeforeISCSIAdmUpdate", "iscsi") duringPurgeOneLunBeforeFileWrite = fiji.Register("duringPurgeOneLunBeforeFileWrite", "iscsi") beforeIscsiLogout = fiji.Register("beforeIscsiLogout", "iscsi") @@ -80,6 +80,11 @@ type ISCSI interface { TargetHasMountedDevice(ctx context.Context, targetIQN string) (bool, error) SafeToLogOut(ctx context.Context, hostNumber, sessionNumber int) bool Logout(ctx context.Context, targetIQN, targetPortal string) error + GetDeviceInfoForLUN( + ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, needFSType bool, + ) (*models.ScsiDeviceInfo, error) + PrepareDeviceForRemoval(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, publishInfo *models.VolumePublishInfo, + allPublishInfos []models.VolumePublishInfo, ignoreErrors, force bool) (string, error) } // Exclusion list contains keywords if found in any Target IQN should not be considered for @@ -97,8 +102,8 @@ type OS interface { type Devices interface { WaitForDevice(ctx context.Context, device string) error GetDeviceFSType(ctx context.Context, device string) (string, error) - NewLUKSDevice(rawDevicePath, volumeId string) (models.LUKSDeviceInterface, error) - EnsureLUKSDeviceMappedOnHost(ctx context.Context, luksDevice models.LUKSDeviceInterface, name string, + NewLUKSDevice(rawDevicePath, volumeId string) (luks.Device, error) + EnsureLUKSDeviceMappedOnHost(ctx context.Context, luksDevice luks.Device, name string, secrets map[string]string) (bool, error) IsDeviceUnformatted(ctx context.Context, device string) (bool, error) EnsureDeviceReadable(ctx context.Context, device string) error @@ -126,9 +131,6 @@ type Devices interface { ) (*models.ScsiDeviceInfo, error) EnsureLUKSDeviceClosed(ctx context.Context, devicePath string) error EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error - PrepareDeviceForRemoval(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, publishInfo *models.VolumePublishInfo, - allPublishInfos []models.VolumePublishInfo, ignoreErrors, force bool) (string, error) - NewLUKSDeviceFromMappingPath(ctx context.Context, mappingPath, volumeId string) (models.LUKSDeviceInterface, error) } type Client struct { @@ -136,14 +138,14 @@ type Client struct { command tridentexec.Command selfHealingExclusion []string osClient OS - deviceClient Devices + devices devices.Devices fileSystemClient filesystem.Filesystem mountClient mount.Mount iscsiUtils IscsiReconcileUtils os afero.Afero } -func New(osClient OS, deviceClient Devices) (*Client, error) { +func New(osClient OS) (*Client, error) { chrootPathPrefix := "" if os.Getenv("DOCKER_PLUGIN_MODE") != "" { chrootPathPrefix = "/host" @@ -158,12 +160,13 @@ func New(osClient OS, deviceClient Devices) (*Client, error) { fsClient := filesystem.New(mountClient) + devicesClient := devices.New() return NewDetailed(chrootPathPrefix, tridentexec.NewCommand(), DefaultSelfHealingExclusion, osClient, - deviceClient, fsClient, mountClient, reconcileutils, osUtils), nil + devicesClient, fsClient, mountClient, reconcileutils, osUtils), nil } func NewDetailed(chrootPathPrefix string, command tridentexec.Command, selfHealingExclusion []string, osClient OS, - deviceClient Devices, fileSystemClient filesystem.Filesystem, mountClient mount.Mount, + devices devices.Devices, fileSystemClient filesystem.Filesystem, mountClient mount.Mount, iscsiUtils IscsiReconcileUtils, os afero.Afero, ) *Client { @@ -171,7 +174,7 @@ func NewDetailed(chrootPathPrefix string, command tridentexec.Command, selfHeali chrootPathPrefix: chrootPathPrefix, command: command, osClient: osClient, - deviceClient: deviceClient, + devices: devices, fileSystemClient: fileSystemClient, mountClient: mountClient, iscsiUtils: iscsiUtils, @@ -319,7 +322,7 @@ func (client *Client) AttachVolume( } // Lookup all the SCSI device information - deviceInfo, err := client.getDeviceInfoForLUN(ctx, hostSessionMap, lunID, publishInfo.IscsiTargetIQN, false) + deviceInfo, err := client.GetDeviceInfoForLUN(ctx, hostSessionMap, lunID, publishInfo.IscsiTargetIQN, false) if err != nil { return mpathSize, fmt.Errorf("error getting iSCSI device information: %v", err) } else if deviceInfo == nil { @@ -356,7 +359,8 @@ func (client *Client) AttachVolume( // Once the multipath device has been found, compare its size with // the size of one of the devices, if it differs then mark it for // resize after the staging. - correctMpathSize, mpathSizeCorrect, err := client.verifyMultipathDeviceSize(ctx, deviceToUse, deviceInfo.Devices[0]) + correctMpathSize, mpathSizeCorrect, err := client.devices.VerifyMultipathDeviceSize(ctx, deviceToUse, + deviceInfo.Devices[0]) if err != nil { Logc(ctx).WithFields(LogFields{ "scsiLun": deviceInfo.LUN, @@ -382,7 +386,7 @@ func (client *Client) AttachVolume( } devicePath := "/dev/" + deviceToUse - if err := client.deviceClient.WaitForDevice(ctx, devicePath); err != nil { + if err := client.devices.WaitForDevice(ctx, devicePath); err != nil { return mpathSize, fmt.Errorf("could not find device %v; %s", devicePath, err) } @@ -399,8 +403,8 @@ func (client *Client) AttachVolume( publishInfo.DevicePath = devicePath if isLUKSDevice { - luksDevice, _ := client.deviceClient.NewLUKSDevice(devicePath, name) - luksFormatted, err = client.deviceClient.EnsureLUKSDeviceMappedOnHost(ctx, luksDevice, name, secrets) + luksDevice := luks.NewLUKSDevice(devicePath, name, client.command) + luksFormatted, err = luksDevice.EnsureLUKSDeviceMappedOnHost(ctx, name, secrets) if err != nil { return mpathSize, err } @@ -412,13 +416,13 @@ func (client *Client) AttachVolume( return mpathSize, nil } - existingFstype, err := client.deviceClient.GetDeviceFSType(ctx, devicePath) + existingFstype, err := client.devices.GetDeviceFSType(ctx, devicePath) if err != nil { return mpathSize, err } if existingFstype == "" { if !isLUKSDevice { - if unformatted, err := client.deviceClient.IsDeviceUnformatted(ctx, devicePath); err != nil { + if unformatted, err := client.devices.IsDeviceUnformatted(ctx, devicePath); err != nil { Logc(ctx).WithField( "device", devicePath, ).WithError(err).Errorf("Unable to identify if the device is unformatted.") @@ -442,7 +446,7 @@ func (client *Client) AttachVolume( if err = client.fileSystemClient.FormatVolume(ctx, devicePath, publishInfo.FilesystemType, publishInfo.FormatOptions); err != nil { return mpathSize, fmt.Errorf("error formatting LUN %s, device %s: %v", name, deviceToUse, err) } - } else if existingFstype != unknownFstype && existingFstype != publishInfo.FilesystemType { + } else if existingFstype != filesystem.UnknownFstype && existingFstype != publishInfo.FilesystemType { Logc(ctx).WithFields(LogFields{ "volume": name, "existingFstype": existingFstype, @@ -577,14 +581,14 @@ func (client *Client) RescanDevices(ctx context.Context, targetIQN string, lunID if len(hostSessionMap) == 0 { return fmt.Errorf("error getting iSCSI device information: no host session found") } - deviceInfo, err := client.getDeviceInfoForLUN(ctx, hostSessionMap, int(lunID), targetIQN, false) + deviceInfo, err := client.GetDeviceInfoForLUN(ctx, hostSessionMap, int(lunID), targetIQN, false) if err != nil { return fmt.Errorf("error getting iSCSI device information: %s", err) } allLargeEnough := true for _, diskDevice := range deviceInfo.Devices { - size, err := client.deviceClient.GetISCSIDiskSize(ctx, DevPrefix+diskDevice) + size, err := client.devices.GetDiskSize(ctx, devices.DevPrefix+diskDevice) if err != nil { return err } @@ -604,7 +608,7 @@ func (client *Client) RescanDevices(ctx context.Context, targetIQN string, lunID if !allLargeEnough { time.Sleep(time.Second) for _, diskDevice := range deviceInfo.Devices { - size, err := client.deviceClient.GetISCSIDiskSize(ctx, DevPrefix+diskDevice) + size, err := client.devices.GetDiskSize(ctx, devices.DevPrefix+diskDevice) if err != nil { return err } @@ -617,7 +621,7 @@ func (client *Client) RescanDevices(ctx context.Context, targetIQN string, lunID if deviceInfo.MultipathDevice != "" { multipathDevice := deviceInfo.MultipathDevice - size, err := client.deviceClient.GetISCSIDiskSize(ctx, DevPrefix+multipathDevice) + size, err := client.devices.GetDiskSize(ctx, devices.DevPrefix+multipathDevice) if err != nil { return err } @@ -630,7 +634,7 @@ func (client *Client) RescanDevices(ctx context.Context, targetIQN string, lunID return err } time.Sleep(time.Second) - size, err = client.deviceClient.GetISCSIDiskSize(ctx, DevPrefix+multipathDevice) + size, err = client.devices.GetDiskSize(ctx, devices.DevPrefix+multipathDevice) if err != nil { return err } @@ -653,7 +657,7 @@ func (client *Client) rescanDisk(ctx context.Context, deviceName string) error { Logc(ctx).WithFields(fields).Debug(">>>> iscsi.rescanDisk") defer Logc(ctx).WithFields(fields).Debug("<<<< iscsi.rescanDisk") - client.listAllDevices(ctx) + client.devices.ListAllDevices(ctx) filename := fmt.Sprintf(client.chrootPathPrefix+"/sys/block/%s/device/rescan", deviceName) Logc(ctx).WithField("filename", filename).Debug("Opening file for writing.") @@ -679,7 +683,7 @@ func (client *Client) rescanDisk(ctx context.Context, deviceName string) error { return fmt.Errorf("no data written to %s", filename) } - client.listAllDevices(ctx) + client.devices.ListAllDevices(ctx) return nil } @@ -693,7 +697,7 @@ func (client *Client) reloadMultipathDevice(ctx context.Context, multipathDevice } _, err := client.command.ExecuteWithTimeout(ctx, "multipath", 10*time.Second, true, "-r", - DevPrefix+multipathDevice) + devices.DevPrefix+multipathDevice) if err != nil { Logc(ctx).WithFields(LogFields{ "device": multipathDevice, @@ -726,7 +730,7 @@ func (client *Client) IsAlreadyAttached(ctx context.Context, lunID int, targetIq // getDeviceInfoForLUN finds iSCSI devices using /dev/disk/by-path values. This method should be // called after calling waitForDeviceScan so that the device paths are known to exist. -func (client *Client) getDeviceInfoForLUN( +func (client *Client) GetDeviceInfoForLUN( ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, needFSType bool, ) (*models.ScsiDeviceInfo, error) { fields := LogFields{ @@ -739,16 +743,16 @@ func (client *Client) getDeviceInfoForLUN( paths := client.iscsiUtils.GetSysfsBlockDirsForLUN(lunID, hostSessionMap) - devices, err := client.iscsiUtils.GetDevicesForLUN(paths) + devicesForLUN, err := client.iscsiUtils.GetDevicesForLUN(paths) if err != nil { return nil, err - } else if 0 == len(devices) { + } else if 0 == len(devicesForLUN) { return nil, fmt.Errorf("scan not completed for LUN %d on target %s", lunID, iSCSINodeName) } multipathDevice := "" - for _, device := range devices { - multipathDevice = client.findMultipathDeviceForDevice(ctx, device) + for _, device := range devicesForLUN { + multipathDevice = client.devices.FindMultipathDeviceForDevice(ctx, device) if multipathDevice != "" { break } @@ -756,18 +760,18 @@ func (client *Client) getDeviceInfoForLUN( var devicePath string if multipathDevice != "" { - devicePath = DevPrefix + multipathDevice + devicePath = devices.DevPrefix + multipathDevice } else { - devicePath = DevPrefix + devices[0] + devicePath = devices.DevPrefix + devicesForLUN[0] } fsType := "" if needFSType { - if err = client.deviceClient.EnsureDeviceReadable(ctx, devicePath); err != nil { + if err = client.devices.EnsureDeviceReadable(ctx, devicePath); err != nil { return nil, err } - fsType, err = client.deviceClient.GetDeviceFSType(ctx, devicePath) + fsType, err = client.devices.GetDeviceFSType(ctx, devicePath) if err != nil { return nil, err } @@ -777,14 +781,14 @@ func (client *Client) getDeviceInfoForLUN( "lun": strconv.Itoa(lunID), "multipathDevice": multipathDevice, "fsType": fsType, - "deviceNames": devices, + "deviceNames": devicesForLUN, "hostSessionMap": hostSessionMap, }).Debug("Found SCSI device.") info := &models.ScsiDeviceInfo{ LUN: strconv.Itoa(lunID), MultipathDevice: multipathDevice, - Devices: devices, + Devices: devicesForLUN, DevicePaths: paths, Filesystem: fsType, IQN: iSCSINodeName, @@ -884,7 +888,7 @@ func (client *Client) waitForMultipathDeviceForDevices(ctx context.Context, devi multipathDevice := "" for _, device := range devices { - multipathDevice = client.findMultipathDeviceForDevice(ctx, device) + multipathDevice = client.devices.FindMultipathDeviceForDevice(ctx, device) if multipathDevice != "" { break } @@ -901,25 +905,6 @@ func (client *Client) waitForMultipathDeviceForDevices(ctx context.Context, devi return multipathDevice, nil } -// findMultipathDeviceForDevice finds the devicemapper parent of a device name like /dev/sdx. -func (client *Client) findMultipathDeviceForDevice(ctx context.Context, device string) string { - Logc(ctx).WithField("device", device).Debug(">>>> iscsi.findMultipathDeviceForDevice") - defer Logc(ctx).WithField("device", device).Debug("<<<< iscsi.findMultipathDeviceForDevice") - - holdersDir := client.chrootPathPrefix + "/sys/block/" + device + "/holders" - if dirs, err := client.os.ReadDir(holdersDir); err == nil { - for _, f := range dirs { - name := f.Name() - if strings.HasPrefix(name, "dm-") { - return name - } - } - } - - Logc(ctx).WithField("device", device).Debug("Could not find multipath device for device.") - return "" -} - // waitForDeviceScan scans all paths to a specific LUN and waits until all // SCSI disk-by-path devices for that LUN are present on the host. func (client *Client) waitForDeviceScan(ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string) error { @@ -935,7 +920,7 @@ func (client *Client) waitForDeviceScan(ctx context.Context, hostSessionMap map[ hosts = append(hosts, hostNumber) } - if err := client.scanTargetLUN(ctx, lunID, hosts); err != nil { + if err := client.devices.ScanTargetLUN(ctx, lunID, hosts); err != nil { Logc(ctx).WithField("scanError", err).Error("Could not scan for new LUN.") } @@ -966,8 +951,8 @@ func (client *Client) waitForDeviceScan(ctx context.Context, hostSessionMap map[ if _, err := client.command.Execute(ctx, "ls", "-al", "/dev"); err != nil { Logc(ctx).Warnf("Could not run ls -al /dev: %v", err) } - if _, err := client.command.Execute(ctx, "ls", "-al", DevMapperRoot); err != nil { - Logc(ctx).Warnf("Could not run ls -al %s: %v", DevMapperRoot, err) + if _, err := client.command.Execute(ctx, "ls", "-al", devices.DevMapperRoot); err != nil { + Logc(ctx).Warnf("Could not run ls -al %s: %v", devices.DevMapperRoot, err) } if _, err := client.command.Execute(ctx, "ls", "-al", "/dev/disk/by-path"); err != nil { Logc(ctx).Warnf("Could not run ls -al /dev/disk/by-path: %v", err) @@ -996,60 +981,6 @@ func (client *Client) waitForDeviceScan(ctx context.Context, hostSessionMap map[ return nil } -// scanTargetLUN scans a single LUN or all the LUNs on an iSCSI target to discover it. -// If all the LUNs are to be scanned please pass -1 for lunID. -func (client *Client) scanTargetLUN(ctx context.Context, lunID int, hosts []int) error { - fields := LogFields{"hosts": hosts, "lunID": lunID} - Logc(ctx).WithFields(fields).Debug(">>>> iscsi.scanTargetLUN") - defer Logc(ctx).WithFields(fields).Debug("<<<< iscsi.scanTargetLUN") - - var ( - f afero.File - err error - ) - - // By default, scan for all the LUNs - scanCmd := "0 0 -" - if lunID >= 0 { - scanCmd = fmt.Sprintf("0 0 %d", lunID) - } - - client.listAllDevices(ctx) - for _, hostNumber := range hosts { - - filename := fmt.Sprintf(client.chrootPathPrefix+"/sys/class/scsi_host/host%d/scan", hostNumber) - if f, err = client.os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0o200); err != nil { - Logc(ctx).WithField("file", filename).Warning("Could not open file for writing.") - return err - } - - if err = duringScanTargetLunAfterFileOpen.Inject(); err != nil { - return err - } - - if written, err := f.WriteString(scanCmd); err != nil { - Logc(ctx).WithFields(LogFields{"file": filename, "error": err}).Warning("Could not write to file.") - _ = f.Close() - return err - } else if written == 0 { - Logc(ctx).WithField("file", filename).Warning("No data written to file.") - _ = f.Close() - return fmt.Errorf("no data written to %s", filename) - } - - _ = f.Close() - - client.listAllDevices(ctx) - Logc(ctx).WithFields(LogFields{ - "scanCmd": scanCmd, - "scanFile": filename, - "host": hostNumber, - }).Debug("Invoked SCSI scan for host.") - } - - return nil -} - // handleInvalidSerials checks the LUN serial number for each path of a given LUN, and // if it doesn't match the expected value, runs a handler function. func (client *Client) handleInvalidSerials( @@ -1063,7 +994,7 @@ func (client *Client) handleInvalidSerials( paths := client.iscsiUtils.GetSysfsBlockDirsForLUN(lunID, hostSessionMap) for _, path := range paths { - serial, err := client.getLunSerial(ctx, path) + serial, err := client.devices.GetLunSerial(ctx, path) if err != nil { if os.IsNotExist(err) { // LUN either isn't scanned yet, or this kernel @@ -1104,35 +1035,6 @@ func (client *Client) handleInvalidSerials( return nil } -// getLunSerial get Linux's idea of what the LUN serial number is -func (client *Client) getLunSerial(ctx context.Context, path string) (string, error) { - Logc(ctx).WithField("path", path).Debug("Get LUN Serial") - // We're going to read the SCSI VPD page 80 serial number - // information. Linux helpfully provides this through sysfs - // so we don't need to open the device and send the ioctl - // ourselves. - filename := path + "/vpd_pg80" - b, err := client.os.ReadFile(filename) - if err != nil { - return "", err - } - if 4 > len(b) || 0x80 != b[1] { - Logc(ctx).WithFields(LogFields{ - "data": b, - }).Error("VPD page 80 format check failed") - return "", fmt.Errorf("malformed VPD page 80 data") - } - length := int(binary.BigEndian.Uint16(b[2:4])) - if len(b) != length+4 { - Logc(ctx).WithFields(LogFields{ - "actual": len(b), - "expected": length + 4, - }).Error("VPD page 80 length check failed") - return "", fmt.Errorf("incorrect length for VPD page 80 serial number") - } - return string(b[4:]), nil -} - // portalsToLogin checks to see if session to for all the specified portals exist for the specified // target. If a session does not exist for a give portal it is added to list of portals that Trident // needs to login to. @@ -1319,7 +1221,7 @@ func (client *Client) verifyMultipathDeviceSerial( // Multipath UUID contains LUN serial in hex format lunSerialHex := hex.EncodeToString([]byte(lunSerial)) - multipathDeviceUUID, err := client.iscsiUtils.GetMultipathDeviceUUID(multipathDevice) + multipathDeviceUUID, err := client.devices.GetMultipathDeviceUUID(multipathDevice) if err != nil { if errors.IsNotFoundError(err) { // If UUID does not exist, then it is hard to verify the DM serial @@ -1361,33 +1263,6 @@ func (client *Client) verifyMultipathDeviceSerial( return nil } -// verifyMultipathDeviceSize compares the size of the DM device with the size -// of a device to ensure correct DM device has the correct size. -func (client *Client) verifyMultipathDeviceSize( - ctx context.Context, multipathDevice, device string, -) (int64, bool, error) { - deviceSize, err := client.deviceClient.GetISCSIDiskSize(ctx, "/dev/"+device) - if err != nil { - return 0, false, err - } - - mpathSize, err := client.deviceClient.GetISCSIDiskSize(ctx, "/dev/"+multipathDevice) - if err != nil { - return 0, false, err - } - - if deviceSize != mpathSize { - return deviceSize, false, nil - } - - Logc(ctx).WithFields(LogFields{ - "multipathDevice": multipathDevice, - "device": device, - }).Debug("Multipath device size check passed.") - - return 0, true, nil -} - // EnsureSessions this is to make sure that Trident establishes iSCSI sessions with the given list of portals func (client *Client) EnsureSessions(ctx context.Context, publishInfo *models.VolumePublishInfo, portals []string, @@ -1407,7 +1282,7 @@ func (client *Client) EnsureSessions(ctx context.Context, publishInfo *models.Vo loginFailedDueToChap := false for _, portal := range portals { - client.listAllDevices(ctx) + client.devices.ListAllDevices(ctx) formattedPortal := formatPortal(portal) if err := client.ensureTarget(ctx, formattedPortal, publishInfo.IscsiTargetIQN, publishInfo.IscsiUsername, @@ -1538,7 +1413,7 @@ func (client *Client) LoginTarget(ctx context.Context, publishInfo *models.Volum defer Logc(ctx).Debug("<<<< iscsi.LoginTarget") args := []string{"-m", "node", "-T", publishInfo.IscsiTargetIQN, "-p", formatPortal(portal)} - client.listAllDevices(ctx) + client.devices.ListAllDevices(ctx) if publishInfo.UseCHAP { secretsToRedact := map[string]string{ "--value=" + publishInfo.IscsiUsername: "--value=" + REDACTED, @@ -1633,7 +1508,7 @@ func (client *Client) LoginTarget(ctx context.Context, publishInfo *models.Volum return err } - client.listAllDevices(ctx) + client.devices.ListAllDevices(ctx) return nil } @@ -1669,41 +1544,6 @@ func formatPortal(portal string) string { } } -// In the case of iscsi trace debug, log info about session and what devices are present -func (client *Client) listAllDevices(ctx context.Context) { - Logc(ctx).Trace(">>>> iscsi.listAllDevices") - defer Logc(ctx).Trace("<<<< iscsi.listAllDevices") - // Log information about all the devices - dmLog := make([]string, 0) - sdLog := make([]string, 0) - sysLog := make([]string, 0) - entries, _ := client.os.ReadDir(DevPrefix) - for _, entry := range entries { - if strings.HasPrefix(entry.Name(), "dm-") { - dmLog = append(dmLog, entry.Name()) - } - if strings.HasPrefix(entry.Name(), "sd") { - sdLog = append(sdLog, entry.Name()) - } - } - - entries, _ = client.os.ReadDir("/sys/block/") - for _, entry := range entries { - sysLog = append(sysLog, entry.Name()) - } - - // TODO: Call this only when verbose logging requires beyond debug level. - // out1, _ := command.ExecuteWithTimeout(ctx, "multipath", deviceOperationsTimeout, true, "-ll") - // out2, _ := execIscsiadmCommand(ctx, "-m", "session") - Logc(ctx).WithFields(LogFields{ - "/dev/dm-*": dmLog, - "/dev/sd*": sdLog, - "/sys/block/*": sysLog, - // "multipath -ll output": string(out1), - // "iscsiadm -m session output": string(out2), - }).Trace("Listing all iSCSI Devices.") -} - // PreChecks to check if all the required tools are present and configured correctly for the volume // attachment to go through func (client *Client) PreChecks(ctx context.Context) error { @@ -2037,7 +1877,7 @@ func (client *Client) RemoveLUNFromSessions(ctx context.Context, publishInfo *mo // TargetHasMountedDevice returns true if this host has any mounted devices on the specified target. func (client *Client) TargetHasMountedDevice(ctx context.Context, targetIQN string) (bool, error) { - mountedISCSIDevices, err := client.deviceClient.GetMountedISCSIDevices(ctx) + mountedISCSIDevices, err := client.GetMountedISCSIDevices(ctx) if err != nil { return false, err } @@ -2109,7 +1949,7 @@ func (client *Client) Logout(ctx context.Context, targetIQN, targetPortal string Logc(ctx).WithFields(logFields).Debug(">>>> iscsi.Logout") defer Logc(ctx).WithFields(logFields).Debug("<<<< iscsi.Logout") - defer client.listAllDevices(ctx) + defer client.devices.ListAllDevices(ctx) if err := beforeIscsiLogout.Inject(); err != nil { return err } @@ -2122,12 +1962,409 @@ func (client *Client) Logout(ctx context.Context, targetIQN, targetPortal string // another iSCSI client (such as kubelet with and "iscsi" PV) attempting to use // the same node. - client.listAllDevices(ctx) + client.devices.ListAllDevices(ctx) return nil } -type LuksCloseTimeDurations interface { - InitLuksCloseStartTime(device string) - GetLuksCloseDuration(device string) (time.Duration, error) - RemoveLuksCloseDurationTracking(device string) +// GetISCSIDevices returns a list of iSCSI devices that are attached to (but not necessarily mounted on) this host. +func (c *Client) GetISCSIDevices(ctx context.Context, getCredentials bool) ([]*models.ScsiDeviceInfo, error) { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + + Logc(ctx).Debug(">>>> devices.GetISCSIDevices") + defer Logc(ctx).Debug("<<<< devices.GetISCSIDevices") + + devices := make([]*models.ScsiDeviceInfo, 0) + hostSessionMapCache := make(map[string]map[int]int) + + // Start by reading the sessions from /sys/class/iscsi_session + sysPath := c.chrootPathPrefix + "/sys/class/iscsi_session/" + sessionDirs, err := os.ReadDir(sysPath) + if err != nil { + Logc(ctx).WithField("error", err).Errorf("Could not read %s", sysPath) + return nil, err + } + + // Loop through each of the iSCSI sessions + for _, sessionDir := range sessionDirs { + + var sessionNumber int + var iscsiChapInfo models.IscsiChapInfo + sessionName := sessionDir.Name() + + if !strings.HasPrefix(sessionName, "session") { + continue + } else if sessionNumber, err = strconv.Atoi(strings.TrimPrefix(sessionName, "session")); err != nil { + Logc(ctx).WithField("session", sessionName).Error("Could not parse session number") + return nil, err + } + + // Find the target IQN and Credentials from the session at /sys/class/iscsi_session/sessionXXX/targetname + sessionPath := sysPath + sessionName + sessionFiles := map[string]string{"targetname": "targetIQN"} + if getCredentials { + sessionFiles["username"] = "IscsiUsername" + sessionFiles["username_in"] = "IscsiTargetUsername" + sessionFiles["password"] = "IscsiInitiatorSecret" + sessionFiles["password_in"] = "IscsiTargetSecret" + } + + sessionValues := make(map[string]string, len(sessionFiles)) + for file, value := range sessionFiles { + path := sessionPath + "/" + file + fileBytes, err := os.ReadFile(path) + if err != nil { + Logc(ctx).WithFields(LogFields{ + "path": path, + "error": err, + }).Errorf("Could not read %v file", file) + return nil, err + } + + // When CHAP not in use instead of empty + // credentials they are "(null)" in sysfs + fileContent := strings.TrimSpace(string(fileBytes)) + if fileContent == "(null)" { + fileContent = "" + } + + sessionValues[value] = fileContent + } + + targetIQN := sessionValues["targetIQN"] + + if getCredentials { + iscsiChapInfo = models.IscsiChapInfo{ + IscsiUsername: sessionValues["IscsiUsername"], + IscsiInitiatorSecret: sessionValues["IscsiInitiatorSecret"], + IscsiTargetUsername: sessionValues["IscsiTargetUsername"], + IscsiTargetSecret: sessionValues["IscsiTargetSecret"], + } + + if iscsiChapInfo != (models.IscsiChapInfo{}) { + iscsiChapInfo.UseCHAP = true + } + } + + Logc(ctx).WithFields(LogFields{ + "targetIQN": targetIQN, + "sessionName": sessionName, + }).Debug("Found iSCSI session / target IQN.") + + // Find the one target at /sys/class/iscsi_session/sessionXXX/device/targetHH:BB:DD (host:bus:device) + sessionDevicePath := sessionPath + "/device/" + targetDirs, err := os.ReadDir(sessionDevicePath) + if err != nil { + Logc(ctx).WithField("error", err).Errorf("Could not read %s", sessionDevicePath) + return nil, err + } + + // Get the one target directory + hostBusDeviceName := "" + targetDirName := "" + for _, targetDir := range targetDirs { + + targetDirName = targetDir.Name() + + if strings.HasPrefix(targetDirName, "target") { + hostBusDeviceName = strings.TrimPrefix(targetDirName, "target") + break + } + } + + if hostBusDeviceName == "" { + Logc(ctx).Warningf("Could not find a host:bus:device directory at %s", sessionDevicePath) + continue + } + + sessionDeviceHBDPath := sessionDevicePath + targetDirName + "/" + + Logc(ctx).WithFields(LogFields{ + "hbdPath": sessionDeviceHBDPath, + "hbdName": hostBusDeviceName, + }).Debug("Found host/bus/device path.") + + // Find the devices at /sys/class/iscsi_session/sessionXXX/device/targetHH:BB:DD/HH:BB:DD:LL (host:bus:device:lun) + hostBusDeviceLunDirs, err := os.ReadDir(sessionDeviceHBDPath) + if err != nil { + Logc(ctx).WithField("error", err).Errorf("Could not read %s", sessionDeviceHBDPath) + return nil, err + } + + for _, hostBusDeviceLunDir := range hostBusDeviceLunDirs { + + hostBusDeviceLunDirName := hostBusDeviceLunDir.Name() + if !strings.HasPrefix(hostBusDeviceLunDirName, hostBusDeviceName) { + continue + } + + sessionDeviceHBDLPath := sessionDeviceHBDPath + hostBusDeviceLunDirName + "/" + + Logc(ctx).WithFields(LogFields{ + "hbdlPath": sessionDeviceHBDLPath, + "hbdlName": hostBusDeviceLunDirName, + }).Debug("Found host/bus/device/LUN path.") + + hbdlValues := strings.Split(hostBusDeviceLunDirName, ":") + if len(hbdlValues) != 4 { + Logc(ctx).Errorf("Could not parse values from %s", hostBusDeviceLunDirName) + return nil, err + } + + hostNum := hbdlValues[0] + busNum := hbdlValues[1] + deviceNum := hbdlValues[2] + lunNum := hbdlValues[3] + + blockPath := sessionDeviceHBDLPath + "/block/" + + // Find the block device at /sys/class/iscsi_session/sessionXXX/device/targetHH:BB:DD/HH:BB:DD:LL/block + blockDeviceDirs, err := os.ReadDir(blockPath) + if err != nil { + Logc(ctx).WithField("error", err).Errorf("Could not read %s", blockPath) + return nil, err + } + + for _, blockDeviceDir := range blockDeviceDirs { + + blockDeviceName := blockDeviceDir.Name() + + Logc(ctx).WithField("blockDeviceName", blockDeviceName).Debug("Found block device.") + + // Find multipath device, if any + var slaveDevices []string + multipathDevice := c.devices.FindMultipathDeviceForDevice(ctx, blockDeviceName) + if multipathDevice != "" { + slaveDevices = c.devices.FindDevicesForMultipathDevice(ctx, multipathDevice) + } else { + slaveDevices = []string{blockDeviceName} + } + + // Get the host/session map, using a cached value if available + hostSessionMap, ok := hostSessionMapCache[targetIQN] + if !ok { + hostSessionMap = c.iscsiUtils.GetISCSIHostSessionMapForTarget(ctx, targetIQN) + hostSessionMapCache[targetIQN] = hostSessionMap + } + + Logc(ctx).WithFields(LogFields{ + "host": hostNum, + "lun": lunNum, + "devices": slaveDevices, + "multipathDevice": multipathDevice, + "iqn": targetIQN, + "sessionNumber": sessionNumber, + "CHAPInUse": iscsiChapInfo.UseCHAP, + "hostSessionMap": hostSessionMap, + }).Debug("Found iSCSI device.") + + device := &models.ScsiDeviceInfo{ + Host: hostNum, + Channel: busNum, + Target: deviceNum, + LUN: lunNum, + Devices: slaveDevices, + MultipathDevice: multipathDevice, + IQN: targetIQN, + SessionNumber: sessionNumber, + CHAPInfo: iscsiChapInfo, + } + + devices = append(devices, device) + } + } + } + + return devices, nil +} + +// GetMountedISCSIDevices returns a list of iSCSI devices that are *mounted* on this host. +func (client *Client) GetMountedISCSIDevices(ctx context.Context) ([]*models.ScsiDeviceInfo, error) { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + + Logc(ctx).Debug(">>>> devices.GetMountedISCSIDevices") + defer Logc(ctx).Debug("<<<< devices.GetMountedISCSIDevices") + + procSelfMountinfo, err := client.mountClient.ListProcMountinfo() + if err != nil { + return nil, err + } + + // Get a list of all mounted /dev devices + mountedDevices := make([]string, 0) + for _, procMount := range procSelfMountinfo { + + hasDevMountSourcePrefix := strings.HasPrefix(procMount.MountSource, DevPrefix) + hasPvcMountPoint := strings.Contains(procMount.MountPoint, "/pvc-") + + if !hasPvcMountPoint { + continue + } + + var mountedDevice string + // Resolve any symlinks to get the real device + if hasDevMountSourcePrefix { + device, err := filepath.EvalSymlinks(procMount.MountSource) + if err != nil { + Logc(ctx).Error(err) + continue + } + mountedDevice = strings.TrimPrefix(device, DevPrefix) + } else { + mountedDevice = strings.TrimPrefix(procMount.Root, "/") + } + + mountedDevices = append(mountedDevices, mountedDevice) + } + + // Get all known iSCSI devices + iscsiDevices, err := client.GetISCSIDevices(ctx, false) + if err != nil { + return nil, err + } + + mountedISCSIDevices := make([]*models.ScsiDeviceInfo, 0) + + // For each mounted device, look for a matching iSCSI device + for _, mountedDevice := range mountedDevices { + iSCSIDeviceLoop: + for _, iscsiDevice := range iscsiDevices { + // First look for a multipath device match + if mountedDevice == iscsiDevice.MultipathDevice { + mountedISCSIDevices = append(mountedISCSIDevices, iscsiDevice) + break iSCSIDeviceLoop + + } else { + // Then look for a slave device match + for _, iscsiSlaveDevice := range iscsiDevice.Devices { + if mountedDevice == iscsiSlaveDevice { + mountedISCSIDevices = append(mountedISCSIDevices, iscsiDevice) + break iSCSIDeviceLoop + } + } + } + } + } + + for _, md := range mountedISCSIDevices { + Logc(ctx).WithFields(LogFields{ + "host": md.Host, + "lun": md.LUN, + "devices": md.Devices, + "multipathDevice": md.MultipathDevice, + "iqn": md.IQN, + }).Debug("Found mounted iSCSI device.") + } + + return mountedISCSIDevices, nil +} + +// removeSCSIDevice informs Linux that a device will be removed. The deviceInfo provided only needs +// the devices and multipathDevice fields set. +// IMPORTANT: The unsafe and force arguments have significant ramifications. Setting ignoreErrors=true will cause the +// function to ignore errors, and try to the remove the device even if that results in data loss, data corruption, +// or putting the system into an invalid state. Setting skipFlush=true will cause data loss, as it does not wait for the +// device to flush any remaining data, but this option is provided to avoid an indefinite hang of flush operation in +// case of an end device is in bad state. Setting ignoreErrors=false and skipFlush=false will fail at the first problem +// encountered, so that callers can be assured that a successful return indicates that the device was cleanly removed. +// This is important because while most of the time the top priority is to avoid data +// loss or data corruption, there are times when data loss is unavoidable, or has already +// happened, and in those cases it's better to be able to clean up than to be stuck in an +// endless retry loop. +func (client *Client) removeSCSIDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, ignoreErrors, + skipFlush bool, +) (bool, error) { + client.devices.ListAllDevices(ctx) + + // Flush multipath device + if !skipFlush { + err := client.devices.MultipathFlushDevice(ctx, deviceInfo) + if err != nil { + if errors.IsTimeoutError(err) { + // Proceed to removeDevice(), ignore any errors. + ignoreErrors = true + } else if !ignoreErrors { + return false, err + } + } + } + + // Flush devices + if !skipFlush { + if err := beforeFlushDevice.Inject(); err != nil { + return false, err + } + err := client.devices.FlushDevice(ctx, deviceInfo, ignoreErrors) + if err != nil && !ignoreErrors { + return false, err + } + } + + // Remove device + err := client.devices.RemoveDevice(ctx, deviceInfo.Devices, ignoreErrors) + if err != nil && !ignoreErrors { + return false, err + } + + // Wait for device to be removed. Do not ignore errors here as we need the device removed + // for the force removal of the multipath device to succeed. + err = client.devices.WaitForDevicesRemoval(ctx, DevPrefix, deviceInfo.Devices, + devicesRemovalMaxWaitTime) + if err != nil { + return false, err + } + + client.devices.ListAllDevices(ctx) + + // If ignoreErrors was set to true while entering into this function and + // multipathFlushDevice above is executed successfully then multipath device + // mapping would have been removed there. However, we still may attempt + // executing RemoveMultipathDeviceMapping() one more time because of below + // bool return. In RemoveMultipathDeviceMapping() we swallow error for now. + // In case RemoveMultipathDeviceMapping() changes in future to handle error, + // one may need to revisit the below bool ignoreErrors being set on timeout error + // resulting from multipathFlushDevice() call at the start of this function. + return ignoreErrors || skipFlush, nil +} + +// PrepareDeviceForRemoval informs Linux that a device will be removed, the function +// also verifies that device being removed is correct based on published device path, +// device serial number (if present) or comparing all publications (allPublishInfos) for +// LUN number uniqueness. +func (client *Client) PrepareDeviceForRemoval( + ctx context.Context, deviceInfo *models.ScsiDeviceInfo, publishInfo *models.VolumePublishInfo, + allPublishInfos []models.VolumePublishInfo, ignoreErrors, force bool, +) (string, error) { + GenerateRequestContextForLayer(ctx, LogLayerUtils) + + lunID := int(publishInfo.IscsiLunNumber) + iSCSINodeName := publishInfo.IscsiTargetIQN + + fields := LogFields{ + "lunID": lunID, + "iSCSINodeName": iSCSINodeName, + "chrootPathPrefix": client.chrootPathPrefix, + } + Logc(ctx).WithFields(fields).Debug(">>>> devices.PrepareDeviceForRemoval") + defer Logc(ctx).WithFields(fields).Debug("<<<< devices.PrepareDeviceForRemoval") + + // CSI Case + // We can't verify a multipath device if we couldn't find it in sysfs. + if publishInfo.IscsiTargetPortal != "" && deviceInfo.MultipathDevice != "" { + _, err := client.devices.VerifyMultipathDevice(ctx, publishInfo, allPublishInfos, deviceInfo) + if err != nil { + return "", err + } + } + + var multipathDevice string + performDeferredDeviceRemoval, err := client.removeSCSIDevice(ctx, deviceInfo, ignoreErrors, force) + if performDeferredDeviceRemoval && deviceInfo.MultipathDevice != "" { + multipathDevice = DevPrefix + deviceInfo.MultipathDevice + Logc(ctx).WithFields(LogFields{ + "lunID": lunID, + "multipathDevice": multipathDevice, + }).Debug("Discovered unmapped multipath device when removing SCSI device.") + } + + return multipathDevice, err } diff --git a/utils/iscsi/iscsi_linux_test.go b/utils/iscsi/iscsi_linux_test.go new file mode 100644 index 000000000..205cfdb19 --- /dev/null +++ b/utils/iscsi/iscsi_linux_test.go @@ -0,0 +1,272 @@ +// Copyright 2024 NetApp, Inc. All Rights Reserved. + +package iscsi + +import ( + "context" + "testing" + "time" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + + "github.com/netapp/trident/mocks/mock_utils/mock_devices" + "github.com/netapp/trident/mocks/mock_utils/mock_devices/mock_luks" + mockexec "github.com/netapp/trident/mocks/mock_utils/mock_exec" + "github.com/netapp/trident/mocks/mock_utils/mock_filesystem" + "github.com/netapp/trident/mocks/mock_utils/mock_iscsi" + "github.com/netapp/trident/mocks/mock_utils/mock_mount" + "github.com/netapp/trident/utils/devices" + "github.com/netapp/trident/utils/devices/luks" + tridentexec "github.com/netapp/trident/utils/exec" + "github.com/netapp/trident/utils/filesystem" + "github.com/netapp/trident/utils/models" + "github.com/netapp/trident/utils/mount" +) + +func TestClient_AttachVolume_LUKS(t *testing.T) { + type parameters struct { + chrootPathPrefix string + getCommand func(controller *gomock.Controller) tridentexec.Command + getOSClient func(controller *gomock.Controller) OS + getDeviceClient func(controller *gomock.Controller) devices.Devices + getLuksDevice func(controller *gomock.Controller) luks.Device + getFileSystemClient func(controller *gomock.Controller) filesystem.Filesystem + getMountClient func(controller *gomock.Controller) mount.Mount + getReconcileUtils func(controller *gomock.Controller) IscsiReconcileUtils + getFileSystemUtils func() afero.Fs + + publishInfo models.VolumePublishInfo + volumeName string + volumeMountPoint string + volumeAuthSecrets map[string]string + + assertError assert.ErrorAssertionFunc + expectedMpathSize int64 + } + + const targetIQN = "iqn.2016-04.com.open-iscsi:ef9f41e2ffa7:vs.3" + const vpdpg80Serial = "SYA5GZFJ8G1M905GVH7H" + + const iscsiadmSessionOutput = `tcp: [3] 127.0.0.1:3260,1028 ` + targetIQN + ` (non-flash) +tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` + const iscsiadmSessionOutputOneSession = "tcp: [3] 127.0.0.1:3260,1028 " + targetIQN + " (non-flash)" + + const iscsiadmNodeOutput = `127.0.0.1:3260,1042 ` + targetIQN + ` +127.0.0.1:3260,1043 ` + targetIQN + ` +` + const iscsiadmDiscoveryDBSendTargetsOutput = `127.0.0.1:3260,1042 ` + targetIQN + ` +127.0.0.1:3260,1043 ` + targetIQN + ` +` + + tests := map[string]parameters{ + "LUKS volume with file system raw": { + chrootPathPrefix: "", + getCommand: func(controller *gomock.Controller) tridentexec.Command { + mockCommand := mockexec.NewMockCommand(controller) + mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-V").Return(nil, nil) + mockCommand.EXPECT().Execute(context.TODO(), "pgrep", "multipathd").Return([]byte("150"), nil) + mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipathd", 5*time.Second, false, "show", + "config").Return([]byte(multipathConfig("no", false)), nil) + mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-m", + "session").Return([]byte(iscsiadmSessionOutput), nil) + mockCommand.EXPECT().ExecuteWithTimeoutAndInput(context.TODO(), "cryptsetup", 30*time.Second, true, "", + "status", "/dev/mapper/luks-test-volume") + return mockCommand + }, + getOSClient: func(controller *gomock.Controller) OS { + mockOsClient := mock_iscsi.NewMockOS(controller) + mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) + return mockOsClient + }, + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) + return mockDevices + }, + getLuksDevice: func(controller *gomock.Controller) luks.Device { + luksDevice := mock_luks.NewMockDevice(controller) + luksDevice.EXPECT().MappedDevicePath().Return("/dev/mapper/dm-0") + luksDevice.EXPECT().EnsureLUKSDeviceMappedOnHost(context.TODO(), "test-volume", + map[string]string{}).Return(true, nil) + return luksDevice + }, + getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { + mockFileSystem := mock_filesystem.NewMockFilesystem(controller) + return mockFileSystem + }, + getMountClient: func(controller *gomock.Controller) mount.Mount { + mockMount := mock_mount.NewMockMount(controller) + return mockMount + }, + getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { + mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) + mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), targetIQN). + Return(map[int]int{0: 0}) + mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). + Times(6) + mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) + return mockReconcileUtils + }, + getFileSystemUtils: func() afero.Fs { + fs := afero.NewMemMapFs() + f, err := fs.Create("/dev/sda/vpd_pg80") + assert.NoError(t, err) + + _, err = f.Write(vpdpg80SerialBytes(vpdpg80Serial)) + assert.NoError(t, err) + + _, err = fs.Create("/dev/sda/rescan") + assert.NoError(t, err) + + _, err = fs.Create("/dev/sda/delete") + assert.NoError(t, err) + + err = fs.MkdirAll("/sys/block/sda/holders/dm-0", 777) + assert.NoError(t, err) + return fs + }, + publishInfo: models.VolumePublishInfo{ + LUKSEncryption: "true", + FilesystemType: filesystem.Raw, + VolumeAccessInfo: models.VolumeAccessInfo{ + IscsiAccessInfo: models.IscsiAccessInfo{ + IscsiTargetPortal: "127.0.0.1", + IscsiPortals: []string{"127.0.0.2"}, + IscsiTargetIQN: targetIQN, + IscsiLunSerial: vpdpg80Serial, + }, + }, + }, + volumeName: "test-volume", + volumeMountPoint: "/mnt/test-volume", + volumeAuthSecrets: map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + }, + assertError: assert.NoError, + }, + "LUKS device has no existing file system type and is not LUKS formatted": { + chrootPathPrefix: "", + getCommand: func(controller *gomock.Controller) tridentexec.Command { + mockCommand := mockexec.NewMockCommand(controller) + mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-V").Return(nil, nil) + mockCommand.EXPECT().Execute(context.TODO(), "pgrep", "multipathd").Return([]byte("150"), nil) + mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipathd", 5*time.Second, false, "show", + "config").Return([]byte(multipathConfig("no", false)), nil) + mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-m", + "session").Return([]byte(iscsiadmSessionOutput), nil) + mockCommand.EXPECT().ExecuteWithTimeoutAndInput(context.TODO(), "cryptsetup", 30*time.Second, true, "", + "status", + "/dev/mapper/luks-test-volume") + return mockCommand + }, + getOSClient: func(controller *gomock.Controller) OS { + mockOsClient := mock_iscsi.NewMockOS(controller) + mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) + return mockOsClient + }, + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) + mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/mapper/luks-test-volume").Return("", nil) + return mockDevices + }, + getLuksDevice: func(controller *gomock.Controller) luks.Device { + luksDevice := mock_luks.NewMockDevice(controller) + luksDevice.EXPECT().MappedDevicePath().Return("/dev/mapper/dm-0") + luksDevice.EXPECT().EnsureLUKSDeviceMappedOnHost(context.TODO(), "test-volume", + map[string]string{}).Return(false, nil) + return luksDevice + }, + getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { + mockFileSystem := mock_filesystem.NewMockFilesystem(controller) + mockFileSystem.EXPECT().FormatVolume(context.TODO(), "/dev/mapper/luks-test-volume", filesystem.Ext4, "") + mockFileSystem.EXPECT().RepairVolume(context.TODO(), "/dev/mapper/luks-test-volume", filesystem.Ext4) + return mockFileSystem + }, + getMountClient: func(controller *gomock.Controller) mount.Mount { + mockMount := mock_mount.NewMockMount(controller) + mockMount.EXPECT().IsMounted(context.TODO(), "/dev/mapper/luks-test-volume", "", "") + mockMount.EXPECT().MountDevice(context.TODO(), "/dev/mapper/luks-test-volume", "/mnt/test-volume", "", false) + return mockMount + }, + getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { + mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) + mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), targetIQN). + Return(map[int]int{0: 0}) + mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). + Times(6) + mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) + return mockReconcileUtils + }, + getFileSystemUtils: func() afero.Fs { + fs := afero.NewMemMapFs() + f, err := fs.Create("/dev/sda/vpd_pg80") + assert.NoError(t, err) + + _, err = f.Write(vpdpg80SerialBytes(vpdpg80Serial)) + assert.NoError(t, err) + + _, err = fs.Create("/dev/sda/rescan") + assert.NoError(t, err) + + _, err = fs.Create("/dev/sda/delete") + assert.NoError(t, err) + + err = fs.MkdirAll("/sys/block/sda/holders/dm-0", 777) + assert.NoError(t, err) + return fs + }, + publishInfo: models.VolumePublishInfo{ + LUKSEncryption: "true", + FilesystemType: filesystem.Ext4, + VolumeAccessInfo: models.VolumeAccessInfo{ + IscsiAccessInfo: models.IscsiAccessInfo{ + IscsiTargetPortal: "127.0.0.1", + IscsiPortals: []string{"127.0.0.2"}, + IscsiTargetIQN: targetIQN, + IscsiLunSerial: vpdpg80Serial, + }, + }, + }, + volumeName: "test-volume", + volumeMountPoint: "/mnt/test-volume", + volumeAuthSecrets: map[string]string{ + "luks-passphrase": "secretA", + "luks-passphrase-name": "A", + }, + assertError: assert.NoError, + }, + } + + for name, params := range tests { + t.Run(name, func(t *testing.T) { + ctrl := gomock.NewController(t) + iscsiClient := NewDetailed(params.chrootPathPrefix, params.getCommand(ctrl), DefaultSelfHealingExclusion, + params.getOSClient(ctrl), params.getDeviceClient(ctrl), params.getFileSystemClient(ctrl), + params.getMountClient(ctrl), params.getReconcileUtils(ctrl), afero.Afero{Fs: params.getFileSystemUtils()}) + + mpathSize, err := iscsiClient.AttachVolume(context.TODO(), params.volumeName, params.volumeMountPoint, + ¶ms.publishInfo, params.volumeAuthSecrets) + if params.assertError != nil { + params.assertError(t, err) + } + + assert.Equal(t, params.expectedMpathSize, mpathSize) + }) + } +} diff --git a/utils/iscsi/iscsi_test.go b/utils/iscsi/iscsi_test.go index 433c1aa3e..da51e9851 100644 --- a/utils/iscsi/iscsi_test.go +++ b/utils/iscsi/iscsi_test.go @@ -15,11 +15,14 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" + "github.com/netapp/trident/mocks/mock_utils/mock_devices" + "github.com/netapp/trident/mocks/mock_utils/mock_devices/mock_luks" mockexec "github.com/netapp/trident/mocks/mock_utils/mock_exec" "github.com/netapp/trident/mocks/mock_utils/mock_filesystem" "github.com/netapp/trident/mocks/mock_utils/mock_iscsi" - "github.com/netapp/trident/mocks/mock_utils/mock_models/mock_luks" "github.com/netapp/trident/mocks/mock_utils/mock_mount" + "github.com/netapp/trident/utils/devices" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" tridentexec "github.com/netapp/trident/utils/exec" "github.com/netapp/trident/utils/filesystem" @@ -30,7 +33,6 @@ import ( func TestNew(t *testing.T) { ctrl := gomock.NewController(t) osClient := mock_iscsi.NewMockOS(ctrl) - devicesClient := mock_iscsi.NewMockDevices(ctrl) type parameters struct { setUpEnvironment func() @@ -50,7 +52,7 @@ func TestNew(t *testing.T) { params.setUpEnvironment() } - iscsiClient, err := New(osClient, devicesClient) + iscsiClient, err := New(osClient) assert.NoError(t, err) assert.NotNil(t, iscsiClient) }) @@ -61,7 +63,7 @@ func TestNewDetailed(t *testing.T) { const chrootPathPrefix = "" ctrl := gomock.NewController(t) osClient := mock_iscsi.NewMockOS(ctrl) - devicesClient := mock_iscsi.NewMockDevices(ctrl) + devicesClient := mock_devices.NewMockDevices(ctrl) FileSystemClient := mock_filesystem.NewMockFilesystem(ctrl) mountClient := mock_mount.NewMockMount(ctrl) command := mockexec.NewMockCommand(ctrl) @@ -75,7 +77,7 @@ func TestClient_AttachVolumeRetry(t *testing.T) { chrootPathPrefix string getCommand func(controller *gomock.Controller) tridentexec.Command getOSClient func(controller *gomock.Controller) OS - getDeviceClient func(controller *gomock.Controller) Devices + getDeviceClient func(controller *gomock.Controller) devices.Devices getFileSystemClient func(controller *gomock.Controller) filesystem.Filesystem getMountClient func(controller *gomock.Controller) mount.Mount getReconcileUtils func(controller *gomock.Controller) IscsiReconcileUtils @@ -135,10 +137,13 @@ tcp: [4] 127.0.0.1:3260,1029 iqn.2016-04.com.open-iscsi:ef9f41e2ffa7:vs.3 (non-f mockOS.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOS }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(4) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(filesystem.Ext4, nil) return mockDevices @@ -192,8 +197,8 @@ tcp: [4] 127.0.0.1:3260,1029 iqn.2016-04.com.open-iscsi:ef9f41e2ffa7:vs.3 (non-f mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -230,8 +235,8 @@ tcp: [4] 127.0.0.1:3260,1029 iqn.2016-04.com.open-iscsi:ef9f41e2ffa7:vs.3 (non-f mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -277,7 +282,8 @@ func TestClient_AttachVolume(t *testing.T) { chrootPathPrefix string getCommand func(controller *gomock.Controller) tridentexec.Command getOSClient func(controller *gomock.Controller) OS - getDeviceClient func(controller *gomock.Controller) Devices + getDeviceClient func(controller *gomock.Controller) devices.Devices + getLuksDevice func(controller *gomock.Controller) luks.Device getFileSystemClient func(controller *gomock.Controller) filesystem.Filesystem getMountClient func(controller *gomock.Controller) mount.Mount getReconcileUtils func(controller *gomock.Controller) IscsiReconcileUtils @@ -318,8 +324,8 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -360,8 +366,8 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -404,8 +410,8 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -448,8 +454,8 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -493,8 +499,8 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -544,8 +550,9 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -606,8 +613,9 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(1) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -666,8 +674,9 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient := mock_iscsi.NewMockOS(controller) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(2) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -742,8 +751,10 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(false, errors.New("some error")) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(2) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -810,8 +821,10 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -878,8 +891,10 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -947,8 +962,11 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1020,8 +1038,12 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("", errors.New("some error")) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1039,7 +1061,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("", errors.New("some error")) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1093,10 +1114,15 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), - errors.New("some error")) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) + mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(errors.New("some error")) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1114,7 +1140,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1168,11 +1193,15 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(errors.New("some error")) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1190,7 +1219,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1244,11 +1272,15 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1266,7 +1298,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1321,16 +1352,23 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - mockDevices.EXPECT().NewLUKSDevice("/dev/dm-0", "test-volume").Return(nil, nil) - mockDevices.EXPECT().EnsureLUKSDeviceMappedOnHost(context.TODO(), nil, "test-volume", - map[string]string{}).Return(false, errors.New("some error")) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, + getLuksDevice: func(controller *gomock.Controller) luks.Device { + luksDevice := mock_luks.NewMockDevice(controller) + luksDevice.EXPECT().EnsureLUKSDeviceMappedOnHost(context.TODO(), "test-volume", + map[string]string{}).Return(false, errors.New("some error")) + return luksDevice + }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { mockFileSystem := mock_filesystem.NewMockFilesystem(controller) return mockFileSystem @@ -1346,7 +1384,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1384,7 +1421,7 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` volumeAuthSecrets: make(map[string]string, 0), assertError: assert.Error, }, - "LUKS volume with file system raw": { + "failed to get file system type of device": { chrootPathPrefix: "", getCommand: func(controller *gomock.Controller) tridentexec.Command { mockCommand := mockexec.NewMockCommand(controller) @@ -1401,105 +1438,23 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - - device := mock_luks.NewMockLUKSDeviceInterface(controller) - device.EXPECT().MappedDevicePath().Return("/dev/mapper/dm-0") - mockDevices.EXPECT().NewLUKSDevice("/dev/dm-0", "test-volume").Return(device, nil) - mockDevices.EXPECT().EnsureLUKSDeviceMappedOnHost(context.TODO(), device, "test-volume", - map[string]string{}).Return(true, nil) - + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, - getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { - mockFileSystem := mock_filesystem.NewMockFilesystem(controller) - return mockFileSystem - }, - getMountClient: func(controller *gomock.Controller) mount.Mount { - mockMount := mock_mount.NewMockMount(controller) - return mockMount - }, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), targetIQN). - Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). - Times(6) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) - return mockReconcileUtils - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - f, err := fs.Create("/dev/sda/vpd_pg80") - assert.NoError(t, err) - - _, err = f.Write(vpdpg80SerialBytes(vpdpg80Serial)) - assert.NoError(t, err) - - _, err = fs.Create("/dev/sda/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/dev/sda/delete") - assert.NoError(t, err) - - err = fs.MkdirAll("/sys/block/sda/holders/dm-0", 777) - assert.NoError(t, err) - return fs - }, - publishInfo: models.VolumePublishInfo{ - LUKSEncryption: "true", - FilesystemType: filesystem.Raw, - VolumeAccessInfo: models.VolumeAccessInfo{ - IscsiAccessInfo: models.IscsiAccessInfo{ - IscsiTargetPortal: "127.0.0.1", - IscsiPortals: []string{"127.0.0.2"}, - IscsiTargetIQN: targetIQN, - IscsiLunSerial: vpdpg80Serial, - }, - }, - }, - volumeName: "test-volume", - volumeMountPoint: "/mnt/test-volume", - volumeAuthSecrets: make(map[string]string, 0), - assertError: assert.NoError, - }, - "failed to get file system type of device": { - chrootPathPrefix: "", - getCommand: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-V").Return(nil, nil) - mockCommand.EXPECT().Execute(context.TODO(), "pgrep", "multipathd").Return([]byte("150"), nil) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipathd", 5*time.Second, false, "show", - "config").Return([]byte(multipathConfig("no", false)), nil) - mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-m", - "session").Return([]byte(iscsiadmSessionOutput), nil) - return mockCommand - }, - getOSClient: func(controller *gomock.Controller) OS { - mockOsClient := mock_iscsi.NewMockOS(controller) - mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) - return mockOsClient - }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) - mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - - device := mock_luks.NewMockLUKSDeviceInterface(controller) - device.EXPECT().MappedDevicePath().Return("/dev/mapper/dm-0") - mockDevices.EXPECT().NewLUKSDevice("/dev/dm-0", "test-volume").Return(device, nil) - mockDevices.EXPECT().EnsureLUKSDeviceMappedOnHost(context.TODO(), device, "test-volume", + getLuksDevice: func(controller *gomock.Controller) luks.Device { + luksDevice := mock_luks.NewMockDevice(controller) + luksDevice.EXPECT().MappedDevicePath().Return("/dev/mapper/dm-0") + luksDevice.EXPECT().EnsureLUKSDeviceMappedOnHost(context.TODO(), "test-volume", map[string]string{}).Return(true, nil) - - mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/mapper/dm-0").Return("", errors.New("some error")) - - return mockDevices + return luksDevice }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { mockFileSystem := mock_filesystem.NewMockFilesystem(controller) @@ -1516,7 +1471,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1554,92 +1508,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` volumeAuthSecrets: make(map[string]string, 0), assertError: assert.Error, }, - "LUKS device has no existing file system type and is not LUKS formatted": { - chrootPathPrefix: "", - getCommand: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-V").Return(nil, nil) - mockCommand.EXPECT().Execute(context.TODO(), "pgrep", "multipathd").Return([]byte("150"), nil) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipathd", 5*time.Second, false, "show", - "config").Return([]byte(multipathConfig("no", false)), nil) - mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-m", - "session").Return([]byte(iscsiadmSessionOutput), nil) - return mockCommand - }, - getOSClient: func(controller *gomock.Controller) OS { - mockOsClient := mock_iscsi.NewMockOS(controller) - mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) - return mockOsClient - }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) - mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - - device := mock_luks.NewMockLUKSDeviceInterface(controller) - device.EXPECT().MappedDevicePath().Return("/dev/mapper/dm-0") - mockDevices.EXPECT().NewLUKSDevice("/dev/dm-0", "test-volume").Return(device, nil) - mockDevices.EXPECT().EnsureLUKSDeviceMappedOnHost(context.TODO(), device, "test-volume", - map[string]string{}).Return(false, nil) - - mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/mapper/dm-0").Return("", nil) - - return mockDevices - }, - getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { - mockFileSystem := mock_filesystem.NewMockFilesystem(controller) - return mockFileSystem - }, - getMountClient: func(controller *gomock.Controller) mount.Mount { - mockMount := mock_mount.NewMockMount(controller) - return mockMount - }, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), targetIQN). - Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). - Times(6) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) - return mockReconcileUtils - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - f, err := fs.Create("/dev/sda/vpd_pg80") - assert.NoError(t, err) - - _, err = f.Write(vpdpg80SerialBytes(vpdpg80Serial)) - assert.NoError(t, err) - - _, err = fs.Create("/dev/sda/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/dev/sda/delete") - assert.NoError(t, err) - - err = fs.MkdirAll("/sys/block/sda/holders/dm-0", 777) - assert.NoError(t, err) - return fs - }, - publishInfo: models.VolumePublishInfo{ - LUKSEncryption: "true", - FilesystemType: filesystem.Ext4, - VolumeAccessInfo: models.VolumeAccessInfo{ - IscsiAccessInfo: models.IscsiAccessInfo{ - IscsiTargetPortal: "127.0.0.1", - IscsiPortals: []string{"127.0.0.2"}, - IscsiTargetIQN: targetIQN, - IscsiLunSerial: vpdpg80Serial, - }, - }, - }, - volumeName: "test-volume", - volumeMountPoint: "/mnt/test-volume", - volumeAuthSecrets: make(map[string]string, 0), - assertError: assert.NoError, - }, "failure determining if device is formatted": { chrootPathPrefix: "", getCommand: func(controller *gomock.Controller) tridentexec.Command { @@ -1657,13 +1525,17 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return("", nil) mockDevices.EXPECT().IsDeviceUnformatted(context.TODO(), "/dev/dm-0").Return(false, errors.New("some error")) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1681,7 +1553,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1735,13 +1606,17 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return("", nil) mockDevices.EXPECT().IsDeviceUnformatted(context.TODO(), "/dev/dm-0").Return(false, nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1759,7 +1634,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1813,13 +1687,17 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return("", nil) mockDevices.EXPECT().IsDeviceUnformatted(context.TODO(), "/dev/dm-0").Return(true, nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1838,7 +1716,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1892,12 +1769,16 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(filesystem.Ext3, nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1915,7 +1796,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -1969,12 +1849,16 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(unknownFstype, nil) + mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(filesystem.UnknownFstype, nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -1993,7 +1877,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -2047,12 +1930,16 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(unknownFstype, nil) + mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(filesystem.UnknownFstype, nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -2072,7 +1959,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -2126,12 +2012,16 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(unknownFstype, nil) + mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(filesystem.UnknownFstype, nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -2153,7 +2043,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -2207,12 +2096,16 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) return mockOsClient }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) + mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, + nil) mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(unknownFstype, nil) + mockDevices.EXPECT().GetDeviceFSType(context.TODO(), "/dev/dm-0").Return(filesystem.UnknownFstype, nil) + mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockDevices }, getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { @@ -2234,7 +2127,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). Times(6) mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - mockReconcileUtils.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) return mockReconcileUtils }, getFileSystemUtils: func() afero.Fs { @@ -2351,7 +2243,7 @@ func TestClient_AddSession(t *testing.T) { for name, params := range tests { t.Run(name, func(t *testing.T) { - client, err := New(nil, nil) + client, err := New(nil) assert.NoError(t, err) ctx := context.WithValue(context.TODO(), SessionInfoSource, "test") client.AddSession(ctx, params.sessions, ¶ms.publishInfo, params.volID, @@ -2367,7 +2259,7 @@ func TestClient_RescanDevices(t *testing.T) { minSize int64 getReconcileUtils func(controller *gomock.Controller) IscsiReconcileUtils - getDeviceClient func(controller *gomock.Controller) Devices + getDeviceClient func(controller *gomock.Controller) devices.Devices getCommandClient func(controller *gomock.Controller) tridentexec.Command getFileSystemUtils func() afero.Fs assertError assert.ErrorAssertionFunc @@ -2381,8 +2273,8 @@ func TestClient_RescanDevices(t *testing.T) { getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { return NewReconcileUtils("", nil) }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2405,9 +2297,10 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), errors.New("some error")) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), errors.New("some error")) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2431,9 +2324,11 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(1) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2457,9 +2352,11 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil).Times(2) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil).Times(2) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2485,10 +2382,12 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), errors.New("some error")) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), errors.New("some error")) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2514,10 +2413,11 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil).Times(1) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil).Times(1) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2543,11 +2443,13 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), errors.New("some error")) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), errors.New("some error")) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2576,11 +2478,13 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2609,11 +2513,13 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2644,12 +2550,14 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), errors.New("some error")) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), errors.New("some error")) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2680,11 +2588,13 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil).Times(2) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil).Times(2) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2715,12 +2625,14 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2750,9 +2662,11 @@ func TestClient_RescanDevices(t *testing.T) { mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) return mockReconcileUtils }, - getDeviceClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) + mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) return mockDevices }, getCommandClient: func(controller *gomock.Controller) tridentexec.Command { @@ -2786,6 +2700,7 @@ func TestClient_rescanDisk(t *testing.T) { type parameters struct { getFileSystemUtils func() afero.Fs assertError assert.ErrorAssertionFunc + getDevices func(controller *gomock.Controller) devices.Devices } const deviceName = "sda" tests := map[string]parameters{ @@ -2796,6 +2711,11 @@ func TestClient_rescanDisk(t *testing.T) { assert.NoError(t, err) return fs }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) + return mockDevices + }, assertError: assert.NoError, }, "error opening rescan file": { @@ -2803,6 +2723,11 @@ func TestClient_rescanDisk(t *testing.T) { fs := afero.NewMemMapFs() return fs }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) + return mockDevices + }, assertError: assert.Error, }, "error writing to file": { @@ -2824,6 +2749,11 @@ func TestClient_rescanDisk(t *testing.T) { return fs }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) + return mockDevices + }, assertError: assert.Error, }, "failed writing to file": { @@ -2845,13 +2775,22 @@ func TestClient_rescanDisk(t *testing.T) { return fs }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) + return mockDevices + }, assertError: assert.Error, }, } for name, params := range tests { t.Run(name, func(t *testing.T) { - client := NewDetailed("", nil, nil, nil, nil, nil, nil, nil, afero.Afero{Fs: params.getFileSystemUtils()}) + var deviceClient devices.Devices + if params.getDevices != nil { + deviceClient = params.getDevices(gomock.NewController(t)) + } + client := NewDetailed("", nil, nil, nil, deviceClient, nil, nil, nil, afero.Afero{Fs: params.getFileSystemUtils()}) err := client.rescanDisk(context.TODO(), deviceName) if params.assertError != nil { params.assertError(t, err) @@ -2997,7 +2936,7 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { needFS bool getIscsiUtils func(controller *gomock.Controller) IscsiReconcileUtils - getDevicesClient func(controller *gomock.Controller) Devices + getDevicesClient func(controller *gomock.Controller) devices.Devices getFileSystemUtils func() afero.Fs assertError assert.ErrorAssertionFunc @@ -3020,8 +2959,8 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { mockIscsiUtils.EXPECT().GetDevicesForLUN(gomock.Any()).Return(make([]string, 0), nil) return mockIscsiUtils }, - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(controller) + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(controller) return mockDeviceClient }, getFileSystemUtils: func() afero.Fs { @@ -3039,8 +2978,8 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { mockIscsiUtils.EXPECT().GetDevicesForLUN([]string{devicePath}).Return(nil, errors.New("some error")) return mockIscsiUtils }, - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(controller) + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(controller) return mockDeviceClient }, getFileSystemUtils: func() afero.Fs { @@ -3058,8 +2997,8 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { mockIscsiUtils.EXPECT().GetDevicesForLUN([]string{devicePath}).Return(nil, nil) return mockIscsiUtils }, - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(controller) + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(controller) return mockDeviceClient }, getFileSystemUtils: func() afero.Fs { @@ -3077,8 +3016,9 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { mockIscsiUtils.EXPECT().GetDevicesForLUN([]string{devicePath}).Return([]string{deviceName}, nil) return mockIscsiUtils }, - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(controller) + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(controller) + mockDeviceClient.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("") return mockDeviceClient }, getFileSystemUtils: func() afero.Fs { @@ -3102,9 +3042,10 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { mockIscsiReconcileUtils.EXPECT().GetDevicesForLUN([]string{devicePath}).Return([]string{deviceName}, nil) return mockIscsiReconcileUtils }, - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(controller) + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(controller) mockDeviceClient.EXPECT().EnsureDeviceReadable(context.TODO(), multipathDevicePath).Return(errors.New("some error")) + mockDeviceClient.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") return mockDeviceClient }, getFileSystemUtils: func() afero.Fs { @@ -3126,10 +3067,11 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { mockIscsiReconcileUtils.EXPECT().GetDevicesForLUN([]string{devicePath}).Return([]string{deviceName}, nil) return mockIscsiReconcileUtils }, - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(controller) + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(controller) mockDeviceClient.EXPECT().EnsureDeviceReadable(context.TODO(), multipathDevicePath).Return(nil) mockDeviceClient.EXPECT().GetDeviceFSType(context.TODO(), multipathDevicePath).Return("", errors.New("some error")) + mockDeviceClient.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") return mockDeviceClient }, getFileSystemUtils: func() afero.Fs { @@ -3152,10 +3094,11 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { mockIscsiUtils.EXPECT().GetDevicesForLUN([]string{devicePath}).Return([]string{deviceName}, nil) return mockIscsiUtils }, - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDeviceClient := mock_iscsi.NewMockDevices(controller) + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDeviceClient := mock_devices.NewMockDevices(controller) mockDeviceClient.EXPECT().EnsureDeviceReadable(context.TODO(), multipathDevicePath).Return(nil) mockDeviceClient.EXPECT().GetDeviceFSType(context.TODO(), multipathDevicePath).Return(filesystem.Ext4, nil) + mockDeviceClient.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") return mockDeviceClient }, getFileSystemUtils: func() afero.Fs { @@ -3184,7 +3127,7 @@ func TestClient_getDeviceInfoForLUN(t *testing.T) { afero.Afero{ Fs: params.getFileSystemUtils(), }) - deviceInfo, err := client.getDeviceInfoForLUN(context.TODO(), params.hostSessionMap, params.lunID, + deviceInfo, err := client.GetDeviceInfoForLUN(context.TODO(), params.hostSessionMap, params.lunID, params.iSCSINodeName, params.needFS) if params.assertError != nil { params.assertError(t, err) @@ -3351,6 +3294,7 @@ func TestClient_waitForMultipathDeviceForLUN(t *testing.T) { getIscsiUtils func(controller *gomock.Controller) IscsiReconcileUtils getFileSystemUtils func() afero.Fs assertError assert.ErrorAssertionFunc + getDevices func(controller *gomock.Controller) devices.Devices } const lunID = 0 @@ -3395,6 +3339,11 @@ func TestClient_waitForMultipathDeviceForLUN(t *testing.T) { getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("") + return mockDevices + }, assertError: assert.Error, }, "happy path": { @@ -3410,14 +3359,23 @@ func TestClient_waitForMultipathDeviceForLUN(t *testing.T) { assert.NoError(t, err) return fs }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") + return mockDevices + }, assertError: assert.NoError, }, } for name, params := range tests { t.Run(name, func(t *testing.T) { + var deviceClient devices.Devices ctrl := gomock.NewController(t) - client := NewDetailed("", nil, nil, nil, nil, nil, nil, params.getIscsiUtils(ctrl), afero.Afero{Fs: params.getFileSystemUtils()}) + if params.getDevices != nil { + deviceClient = params.getDevices(ctrl) + } + client := NewDetailed("", nil, nil, nil, deviceClient, nil, nil, params.getIscsiUtils(ctrl), afero.Afero{Fs: params.getFileSystemUtils()}) err := client.waitForMultipathDeviceForLUN(context.TODO(), params.hostSessionMap, lunID, iscsiNodeName) if params.assertError != nil { @@ -3434,6 +3392,7 @@ func TestClient_waitForDeviceScan(t *testing.T) { getCommandClient func(controller *gomock.Controller) tridentexec.Command getOsClient func(controller *gomock.Controller) OS getFileSystemUtils func() afero.Fs + getDevices func(controller *gomock.Controller) devices.Devices assertError assert.ErrorAssertionFunc } @@ -3462,6 +3421,11 @@ func TestClient_waitForDeviceScan(t *testing.T) { getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(gomock.NewController(t)) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{}) + return mockDevices + }, assertError: assert.Error, }, "some devices present": { @@ -3485,6 +3449,11 @@ func TestClient_waitForDeviceScan(t *testing.T) { getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + return mockDevices + }, assertError: assert.NoError, }, "all devices present": { @@ -3507,6 +3476,11 @@ func TestClient_waitForDeviceScan(t *testing.T) { getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + return mockDevices + }, assertError: assert.NoError, }, "no devices present": { @@ -3532,6 +3506,11 @@ func TestClient_waitForDeviceScan(t *testing.T) { getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ScanTargetLUN(context.TODO(), 0, []int{0}) + return mockDevices + }, assertError: assert.Error, }, } @@ -3539,7 +3518,11 @@ func TestClient_waitForDeviceScan(t *testing.T) { for name, params := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) - client := NewDetailed("", params.getCommandClient(ctrl), nil, params.getOsClient(ctrl), nil, nil, nil, + var deviceClient devices.Devices + if params.getDevices != nil { + deviceClient = params.getDevices(ctrl) + } + client := NewDetailed("", params.getCommandClient(ctrl), nil, params.getOsClient(ctrl), deviceClient, nil, nil, params.getIscsiUtils(ctrl), afero.Afero{Fs: params.getFileSystemUtils()}) @@ -3551,94 +3534,6 @@ func TestClient_waitForDeviceScan(t *testing.T) { } } -func TestClient_scanTargetLUN(t *testing.T) { - type parameters struct { - assertError assert.ErrorAssertionFunc - getFileSystemUtils func() afero.Fs - } - - const lunID = 0 - const host1 = 1 - const host2 = 2 - - tests := map[string]parameters{ - "scan files not present": { - getFileSystemUtils: func() afero.Fs { - return afero.NewMemMapFs() - }, - assertError: assert.Error, - }, - "error writing to scan files": { - getFileSystemUtils: func() afero.Fs { - memFs := afero.NewMemMapFs() - _, err := memFs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host1)) - assert.NoError(t, err) - _, err = memFs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host2)) - assert.NoError(t, err) - - f := &aferoFileWrapper{ - WriteStringError: errors.New("some error"), - File: mem.NewFileHandle(&mem.FileData{}), - } - - fs := &aferoWrapper{ - openFileResponse: f, - openResponse: f, - Fs: memFs, - } - - return fs - }, - assertError: assert.Error, - }, - "failed to write to scan files": { - getFileSystemUtils: func() afero.Fs { - memFs := afero.NewMemMapFs() - _, err := memFs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host1)) - assert.NoError(t, err) - _, err = memFs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host2)) - assert.NoError(t, err) - - f := &aferoFileWrapper{ - WriteStringCount: 0, - File: mem.NewFileHandle(&mem.FileData{}), - } - - fs := &aferoWrapper{ - openFileResponse: f, - openResponse: f, - Fs: memFs, - } - - return fs - }, - assertError: assert.Error, - }, - "happy path": { - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host1)) - assert.NoError(t, err) - _, err = fs.Create(fmt.Sprintf("/sys/class/scsi_host/host%d/scan", host2)) - assert.NoError(t, err) - return fs - }, - assertError: assert.NoError, - }, - } - - for name, params := range tests { - t.Run(name, func(t *testing.T) { - client := NewDetailed("", nil, nil, nil, nil, nil, nil, nil, afero.Afero{Fs: params.getFileSystemUtils()}) - - err := client.scanTargetLUN(context.TODO(), lunID, []int{host1, host2}) - if params.assertError != nil { - params.assertError(t, err) - } - }) - } -} - func TestClient_handleInvalidSerials(t *testing.T) { type parameters struct { hostSessionMap map[int]int @@ -3647,6 +3542,7 @@ func TestClient_handleInvalidSerials(t *testing.T) { getIscsiUtils func(controller *gomock.Controller) IscsiReconcileUtils getFileSystemUtils func() afero.Fs + getDevicesClient func(controller *gomock.Controller) devices.Devices assertError assert.ErrorAssertionFunc assertHandlerCalled assert.BoolAssertionFunc @@ -3680,6 +3576,11 @@ func TestClient_handleInvalidSerials(t *testing.T) { getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), devicePath).Return(vpdpg80Serial, nil) + return mockDevices + }, assertError: assert.NoError, }, "error reading serial files": { @@ -3697,6 +3598,11 @@ func TestClient_handleInvalidSerials(t *testing.T) { } return fs }, + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), devicePath).Return("", fmt.Errorf("mock error")) + return mockDevices + }, assertError: assert.Error, }, "lun serial does not match expected serial": { @@ -3715,6 +3621,11 @@ func TestClient_handleInvalidSerials(t *testing.T) { assert.NoError(t, err) return fs }, + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), devicePath).Return(vpdpg80Serial, nil) + return mockDevices + }, assertHandlerCalled: assert.True, handlerError: errors.New("some error"), assertError: assert.Error, @@ -3735,6 +3646,11 @@ func TestClient_handleInvalidSerials(t *testing.T) { assert.NoError(t, err) return fs }, + getDevicesClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetLunSerial(context.TODO(), devicePath).Return(vpdpg80Serial, nil) + return mockDevices + }, assertError: assert.NoError, }, } @@ -3748,7 +3664,13 @@ func TestClient_handleInvalidSerials(t *testing.T) { } ctrl := gomock.NewController(t) - client := NewDetailed("", nil, nil, nil, nil, nil, nil, params.getIscsiUtils(ctrl), afero.Afero{Fs: params.getFileSystemUtils()}) + + var devicesClient devices.Devices + if params.getDevicesClient != nil { + devicesClient = params.getDevicesClient(ctrl) + } + + client := NewDetailed("", nil, nil, nil, devicesClient, nil, nil, params.getIscsiUtils(ctrl), afero.Afero{Fs: params.getFileSystemUtils()}) err := client.handleInvalidSerials(context.TODO(), params.hostSessionMap, lunID, targetIQN, params.expectedSerial, mockHandler) if params.assertError != nil { @@ -3761,87 +3683,6 @@ func TestClient_handleInvalidSerials(t *testing.T) { } } -func TestClient_getLunSerial(t *testing.T) { - type parameters struct { - getFileSystemUtils func() afero.Fs - expectedResponse string - assertError assert.ErrorAssertionFunc - } - - const devicePath = "/dev/sda" - const vpdpg80Serial = "SYA5GZFJ8G1M905GVH7H" - - tests := map[string]parameters{ - "error reading serial file": { - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - return fs - }, - expectedResponse: "", - assertError: assert.Error, - }, - "invalid serial in file len < 4 bytes": { - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - f, err := fs.Create(devicePath + "/vpd_pg80") - assert.NoError(t, err) - _, err = f.Write([]byte("123")) - assert.NoError(t, err) - return fs - }, - expectedResponse: "", - assertError: assert.Error, - }, - "invalid serial bytes[1] != 0x80": { - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - f, err := fs.Create(devicePath + "/vpd_pg80") - assert.NoError(t, err) - _, err = f.Write([]byte{0x81, 0x00, 0x00, 0x00, 0x00}) - assert.NoError(t, err) - return fs - }, - expectedResponse: "", - assertError: assert.Error, - }, - "invalid serial bad length": { - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - f, err := fs.Create(devicePath + "/vpd_pg80") - assert.NoError(t, err) - _, err = f.Write([]byte{0x81, 0x80, 0x01, 0x01, 0x02}) - assert.NoError(t, err) - return fs - }, - expectedResponse: "", - assertError: assert.Error, - }, - "happy path": { - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - f, err := fs.Create(devicePath + "/vpd_pg80") - assert.NoError(t, err) - _, err = f.Write(vpdpg80SerialBytes(vpdpg80Serial)) - assert.NoError(t, err) - return fs - }, - expectedResponse: vpdpg80Serial, - assertError: assert.NoError, - }, - } - - for name, params := range tests { - t.Run(name, func(t *testing.T) { - client := NewDetailed("", nil, nil, nil, nil, nil, nil, nil, afero.Afero{Fs: params.getFileSystemUtils()}) - response, err := client.getLunSerial(context.TODO(), devicePath) - if params.assertError != nil { - params.assertError(t, err) - } - assert.Equal(t, params.expectedResponse, response) - }) - } -} - func TestClient_portalsToLogin(t *testing.T) { type parameters struct { getCommandClient func(controller *gomock.Controller) tridentexec.Command @@ -3946,9 +3787,9 @@ tcp: [4] 127.0.0.2:3260,1029 ` + alternateTargetIQN + ` (non-flash)` func TestClient_verifyMultipathDeviceSerial(t *testing.T) { type parameters struct { - lunSerial string - getIscsiUtils func(controller *gomock.Controller) IscsiReconcileUtils - assertError assert.ErrorAssertionFunc + lunSerial string + getDevices func(controller *gomock.Controller) devices.Devices + assertError assert.ErrorAssertionFunc } const multipathDeviceName = "dm-0" @@ -3958,48 +3799,44 @@ func TestClient_verifyMultipathDeviceSerial(t *testing.T) { tests := map[string]parameters{ "empty lun serial": { - lunSerial: "", - getIscsiUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockIscsiUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - return mockIscsiUtils - }, + lunSerial: "", assertError: assert.NoError, }, "multipath device not present": { lunSerial: vpdpg80Serial, - getIscsiUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockIscsiUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockIscsiUtils.EXPECT().GetMultipathDeviceUUID(multipathDeviceName).Return("", + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetMultipathDeviceUUID(multipathDeviceName).Return("", errors.NotFoundError("not found")) - return mockIscsiUtils + return mockDevices }, assertError: assert.NoError, }, "error getting multipath device UUID": { lunSerial: vpdpg80Serial, - getIscsiUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockIscsiUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockIscsiUtils.EXPECT().GetMultipathDeviceUUID(multipathDeviceName).Return("", + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetMultipathDeviceUUID(multipathDeviceName).Return("", errors.New("some error")) - return mockIscsiUtils + return mockDevices }, assertError: assert.Error, }, "LUN serial not present in multipath device UUID": { lunSerial: vpdpg80Serial, - getIscsiUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockIscsiUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockIscsiUtils.EXPECT().GetMultipathDeviceUUID(multipathDeviceName).Return("foo", nil) - return mockIscsiUtils + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetMultipathDeviceUUID(multipathDeviceName).Return("foo", nil) + return mockDevices }, assertError: assert.Error, }, "happy path": { lunSerial: vpdpg80Serial, - getIscsiUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockIscsiUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockIscsiUtils.EXPECT().GetMultipathDeviceUUID(multipathDeviceName).Return(multipathdeviceSerial, nil) - return mockIscsiUtils + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().GetMultipathDeviceUUID(multipathDeviceName).Return(multipathdeviceSerial, nil) + return mockDevices }, assertError: assert.NoError, }, @@ -4008,86 +3845,15 @@ func TestClient_verifyMultipathDeviceSerial(t *testing.T) { for name, params := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) - client := NewDetailed("", nil, nil, nil, nil, nil, nil, params.getIscsiUtils(ctrl), afero.Afero{}) - err := client.verifyMultipathDeviceSerial(context.TODO(), multipathDeviceName, params.lunSerial) - if params.assertError != nil { - params.assertError(t, err) + var devicesClient devices.Devices + if params.getDevices != nil { + devicesClient = params.getDevices(ctrl) } - }) - } -} - -func TestClient_verifyMultipathDeviceSize(t *testing.T) { - type parameters struct { - getDevicesClient func(controller *gomock.Controller) Devices - assertError assert.ErrorAssertionFunc - assertValid assert.BoolAssertionFunc - expectedDeviceSize int64 - } - - const deviceName = "sda" - const multipathDeviceName = "dm-0" - - tests := map[string]parameters{ - "error getting device size": { - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(0), - errors.New("some error")) - return mockDevices - }, - assertError: assert.Error, - assertValid: assert.False, - expectedDeviceSize: 0, - }, - "error getting multipath device size": { - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(0), - errors.New("some error")) - return mockDevices - }, - assertError: assert.Error, - assertValid: assert.False, - expectedDeviceSize: 0, - }, - "device size != multipath device size": { - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(0), nil) - return mockDevices - }, - assertError: assert.NoError, - assertValid: assert.False, - expectedDeviceSize: 1, - }, - "happy path": { - getDevicesClient: func(controller *gomock.Controller) Devices { - mockDevices := mock_iscsi.NewMockDevices(controller) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(1), nil) - mockDevices.EXPECT().GetISCSIDiskSize(context.TODO(), gomock.Any()).Return(int64(1), nil) - return mockDevices - }, - assertError: assert.NoError, - assertValid: assert.True, - expectedDeviceSize: 0, - }, - } - - for name, params := range tests { - t.Run(name, func(t *testing.T) { - ctrl := gomock.NewController(t) - client := NewDetailed("", nil, nil, nil, params.getDevicesClient(ctrl), nil, nil, nil, afero.Afero{}) - deviceSize, valid, err := client.verifyMultipathDeviceSize(context.TODO(), multipathDeviceName, deviceName) + client := NewDetailed("", nil, nil, nil, devicesClient, nil, nil, nil, afero.Afero{}) + err := client.verifyMultipathDeviceSerial(context.TODO(), multipathDeviceName, params.lunSerial) if params.assertError != nil { params.assertError(t, err) } - if params.assertValid != nil { - params.assertValid(t, valid) - } - assert.Equal(t, params.expectedDeviceSize, deviceSize) }) } } @@ -4097,6 +3863,7 @@ func TestClient_EnsureSessions(t *testing.T) { publishInfo models.VolumePublishInfo portals []string getCommandClient func(controller *gomock.Controller) tridentexec.Command + getDevices func(controller *gomock.Controller) devices.Devices getFileSystemUtils func() afero.Fs assertError assert.ErrorAssertionFunc assertResult assert.BoolAssertionFunc @@ -4134,6 +3901,11 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-m", "node").Return(nil, errors.New("some error")) return mockCommand }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) + return mockDevices + }, getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, @@ -4161,6 +3933,11 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` return mockCommand }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) + return mockDevices + }, getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, @@ -4191,6 +3968,11 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` return mockCommand }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) + return mockDevices + }, getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, @@ -4228,6 +4010,11 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` return mockCommand }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(3) + return mockDevices + }, getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, @@ -4265,6 +4052,11 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` return mockCommand }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(3) + return mockDevices + }, getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, @@ -4302,6 +4094,11 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` return mockCommand }, + getDevices: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(3) + return mockDevices + }, getFileSystemUtils: func() afero.Fs { return afero.NewMemMapFs() }, @@ -4313,7 +4110,11 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` for name, params := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) - client := NewDetailed("", params.getCommandClient(ctrl), nil, nil, nil, nil, nil, nil, + var devicesClient devices.Devices + if params.getDevices != nil { + devicesClient = params.getDevices(ctrl) + } + client := NewDetailed("", params.getCommandClient(ctrl), nil, nil, devicesClient, nil, nil, nil, afero.Afero{Fs: params.getFileSystemUtils()}) successfullyLoggedIn, err := client.EnsureSessions(context.TODO(), ¶ms.publishInfo, params.portals) if params.assertError != nil { @@ -4332,6 +4133,7 @@ func TestClient_LoginTarget(t *testing.T) { portal string getCommandClient func(controller *gomock.Controller) tridentexec.Command + getDeviceClient func(controller *gomock.Controller) devices.Devices assertError assert.ErrorAssertionFunc } @@ -4356,6 +4158,11 @@ func TestClient_LoginTarget(t *testing.T) { Return(nil, errors.New("some error")) return mockCommand }, + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) + return mockDevices + }, assertError: assert.Error, }, "error setting login max retry": { @@ -4379,6 +4186,11 @@ func TestClient_LoginTarget(t *testing.T) { Return(nil, errors.New("some error")) return mockCommand }, + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) + return mockDevices + }, assertError: assert.Error, }, "error logging in": { @@ -4407,6 +4219,11 @@ func TestClient_LoginTarget(t *testing.T) { Return(nil, errors.New("some error")) return mockCommand }, + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()) + return mockDevices + }, assertError: assert.Error, }, "happy path": { @@ -4435,6 +4252,11 @@ func TestClient_LoginTarget(t *testing.T) { Return(nil, nil) return mockCommand }, + getDeviceClient: func(controller *gomock.Controller) devices.Devices { + mockDevices := mock_devices.NewMockDevices(controller) + mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) + return mockDevices + }, assertError: assert.NoError, }, } @@ -4442,7 +4264,11 @@ func TestClient_LoginTarget(t *testing.T) { for name, params := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) - iscsiClient := NewDetailed("", params.getCommandClient(ctrl), nil, nil, nil, nil, nil, nil, + var deviceClient devices.Devices + if params.getDeviceClient != nil { + deviceClient = params.getDeviceClient(ctrl) + } + iscsiClient := NewDetailed("", params.getCommandClient(ctrl), nil, nil, deviceClient, nil, nil, nil, afero.Afero{Fs: afero.NewMemMapFs()}) err := iscsiClient.LoginTarget(context.TODO(), ¶ms.publishInfo, params.portal) diff --git a/utils/iscsi/reconcile_utils.go b/utils/iscsi/reconcile_utils.go index 774734b63..4b4c4ea64 100644 --- a/utils/iscsi/reconcile_utils.go +++ b/utils/iscsi/reconcile_utils.go @@ -12,16 +12,12 @@ import ( "strings" . "github.com/netapp/trident/logging" - "github.com/netapp/trident/utils/errors" "github.com/netapp/trident/utils/models" ) type IscsiReconcileUtils interface { GetISCSIHostSessionMapForTarget(context.Context, string) map[int]int GetSysfsBlockDirsForLUN(int, map[int]int) []string - GetMultipathDeviceUUID(string) (string, error) - GetMultipathDeviceBySerial(context.Context, string) (string, error) - GetMultipathDeviceDisks(context.Context, string) ([]string, error) GetDevicesForLUN(paths []string) ([]string, error) ReconcileISCSIVolumeInfo(ctx context.Context, trackingInfo *models.VolumeTrackingInfo) (bool, error) } @@ -38,89 +34,6 @@ func NewReconcileUtils(chrootPathPrefix string, osClient OS) IscsiReconcileUtils } } -// GetMultipathDeviceUUID find the /sys/block/dmX/dm/uuid UUID that contains DM device serial in hex format. -func (h *IscsiReconcileHelper) GetMultipathDeviceUUID(multipathDevicePath string) (string, error) { - multipathDevice := strings.TrimPrefix(multipathDevicePath, "/dev/") - - deviceUUIDPath := h.chrootPathPrefix + fmt.Sprintf("/sys/block/%s/dm/uuid", multipathDevice) - - exists, err := h.osClient.PathExists(deviceUUIDPath) - if !exists || err != nil { - return "", errors.NotFoundError("multipath device '%s' UUID not found", multipathDevice) - } - - UUID, err := os.ReadFile(deviceUUIDPath) - if err != nil { - return "", err - } - - return string(UUID), nil -} - -// GetMultipathDeviceDisks find the /sys/block/dmX/slaves/sdX disks. -func (h *IscsiReconcileHelper) GetMultipathDeviceDisks( - ctx context.Context, multipathDevicePath string, -) ([]string, error) { - devices := make([]string, 0) - multipathDevice := strings.TrimPrefix(multipathDevicePath, "/dev/") - - diskPath := h.chrootPathPrefix + fmt.Sprintf("/sys/block/%s/slaves/", multipathDevice) - diskDirs, err := os.ReadDir(diskPath) - if err != nil { - Logc(ctx).WithError(err).Errorf("Could not read %s", diskPath) - return nil, fmt.Errorf("failed to identify multipath device disks; unable to read '%s'", diskPath) - } - - for _, diskDir := range diskDirs { - contentName := diskDir.Name() - if !strings.HasPrefix(contentName, "sd") { - continue - } - - devices = append(devices, contentName) - } - - return devices, nil -} - -// GetMultipathDeviceBySerial find DM device whose UUID /sys/block/dmX/dm/uuid contains serial in hex format. -func (h *IscsiReconcileHelper) GetMultipathDeviceBySerial(ctx context.Context, hexSerial string) (string, error) { - sysPath := h.chrootPathPrefix + "/sys/block/" - - blockDirs, err := os.ReadDir(sysPath) - if err != nil { - Logc(ctx).WithError(err).Errorf("Could not read %s", sysPath) - return "", fmt.Errorf("failed to find multipath device by serial; unable to read '%s'", sysPath) - } - - for _, blockDir := range blockDirs { - dmDeviceName := blockDir.Name() - if !strings.HasPrefix(dmDeviceName, "dm-") { - continue - } - - uuid, err := h.GetMultipathDeviceUUID(dmDeviceName) - if err != nil { - Logc(ctx).WithFields(LogFields{ - "UUID": hexSerial, - "multipathDevice": dmDeviceName, - "err": err, - }).Error("Failed to get UUID of multipath device.") - continue - } - - if strings.Contains(uuid, hexSerial) { - Logc(ctx).WithFields(LogFields{ - "UUID": hexSerial, - "multipathDevice": dmDeviceName, - }).Debug("Found multipath device by UUID.") - return dmDeviceName, nil - } - } - - return "", errors.NotFoundError("no multipath device found") -} - // ReconcileISCSIVolumeInfo returns true if any of the expected conditions for a present volume are true (e.g. the // expected LUN exists). func (h *IscsiReconcileHelper) ReconcileISCSIVolumeInfo( diff --git a/utils/models/types.go b/utils/models/types.go index e4cae460d..a47633be3 100644 --- a/utils/models/types.go +++ b/utils/models/types.go @@ -2,10 +2,7 @@ package models -//go:generate mockgen -destination=../../mocks/mock_utils/mock_models/mock_luks/mock_luks.go -package mock_luks github.com/netapp/trident/utils/models LUKSDeviceInterface - import ( - "context" "fmt" "strings" "time" @@ -936,22 +933,6 @@ func (p *ISCSISessions) GoString() string { return p.String() } -type LUKSDeviceInterface interface { - RawDevicePath() string - MappedDevicePath() string - MappedDeviceName() string - - IsLUKSFormatted(ctx context.Context) (bool, error) - IsOpen(ctx context.Context) (bool, error) - - Open(ctx context.Context, luksPassphrase string) error - LUKSFormat(ctx context.Context, luksPassphrase string) error - EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (bool, error) - RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error - CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) - Resize(ctx context.Context, luksPassphrase string) error -} - // ParseHostportIP returns just the IP address part of the given input IP address and strips any port information func ParseHostportIP(hostport string) string { ipAddress := "" diff --git a/utils/nvme.go b/utils/nvme.go index 6378f6cd4..cb8bca206 100644 --- a/utils/nvme.go +++ b/utils/nvme.go @@ -13,7 +13,9 @@ import ( "go.uber.org/multierr" . "github.com/netapp/trident/logging" + "github.com/netapp/trident/utils/devices/luks" "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/exec" "github.com/netapp/trident/utils/filesystem" "github.com/netapp/trident/utils/models" ) @@ -321,8 +323,8 @@ func NVMeMountVolume( var err error isLUKSDevice := ParseBool(publishInfo.LUKSEncryption) if isLUKSDevice { - luksDevice, _ := NewLUKSDevice(devicePath, name) - luksFormatted, err = EnsureLUKSDeviceMappedOnHost(ctx, luksDevice, name, secrets) + luksDevice := luks.NewLUKSDevice(devicePath, name, exec.NewCommand()) + luksFormatted, err = luksDevice.EnsureLUKSDeviceMappedOnHost(ctx, name, secrets) if err != nil { return err } @@ -346,13 +348,13 @@ func NVMeMountVolume( return nil } - existingFstype, err := getDeviceFSType(ctx, devicePath) + existingFstype, err := devicesClient.GetDeviceFSType(ctx, devicePath) if err != nil { return err } if existingFstype == "" { if !isLUKSDevice { - if unformatted, err := isDeviceUnformatted(ctx, devicePath); err != nil { + if unformatted, err := devicesClient.IsDeviceUnformatted(ctx, devicePath); err != nil { Logc(ctx).WithField( "device", devicePath, ).WithError(err).Error("Unable to identify if the device is not formatted.") @@ -374,7 +376,7 @@ func NVMeMountVolume( if err != nil { return fmt.Errorf("error formatting Namespace %s, device %s: %v", name, devicePath, err) } - } else if existingFstype != unknownFstype && existingFstype != publishInfo.FilesystemType { + } else if existingFstype != filesystem.UnknownFstype && existingFstype != publishInfo.FilesystemType { Logc(ctx).WithFields(LogFields{ "volume": name, "existingFstype": existingFstype, diff --git a/utils/utils.go b/utils/utils.go index 6afa134bc..cff51be90 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -20,7 +20,6 @@ import ( "sort" "strconv" "strings" - "time" "go.uber.org/multierr" "golang.org/x/text/cases" @@ -61,8 +60,6 @@ const ( WindowsPathSeparator = `\` UnixPathSeparator = "/" - deviceOperationsTimeout = 5 * time.Second - TimestampFormat = "2006-01-02T15:04:05Z" )