From c3b1b0d80797335ff4838ea3560ac75a561b7cd9 Mon Sep 17 00:00:00 2001 From: James Munson Date: Thu, 7 Nov 2024 15:41:47 -0700 Subject: [PATCH] fix(resize): add rpc method to resize filesystem Signed-off-by: James Munson --- pkg/client/share_manager_client.go | 11 ++++- pkg/crypto/crypto.go | 18 ++++++++ pkg/rpc/server.go | 68 +++++++++++++++++++++++++++++- pkg/server/share_manager.go | 2 +- 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/pkg/client/share_manager_client.go b/pkg/client/share_manager_client.go index 32e7781e..f6f28106 100644 --- a/pkg/client/share_manager_client.go +++ b/pkg/client/share_manager_client.go @@ -3,12 +3,13 @@ package client import ( "context" - rpc "github.com/longhorn/types/pkg/generated/smrpc" "github.com/pkg/errors" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/types/known/emptypb" + rpc "github.com/longhorn/types/pkg/generated/smrpc" + "github.com/longhorn/longhorn-share-manager/pkg/types" ) @@ -47,6 +48,14 @@ func (c *ShareManagerClient) FilesystemTrim(encryptedDevice bool) error { return err } +func (c *ShareManagerClient) FilesystemResize() error { + ctx, cancel := context.WithTimeout(context.Background(), types.GRPCServiceTimeout) + defer cancel() + + _, err := c.client.FilesystemResize(ctx, &emptypb.Empty{}) + return err +} + func (c *ShareManagerClient) Unmount() error { ctx, cancel := context.WithTimeout(context.Background(), types.GRPCServiceTimeout) defer cancel() diff --git a/pkg/crypto/crypto.go b/pkg/crypto/crypto.go index a2599a70..213d20e7 100644 --- a/pkg/crypto/crypto.go +++ b/pkg/crypto/crypto.go @@ -63,6 +63,24 @@ func CloseVolume(volume string) error { return err } +func ResizeEncryptoDevice(volume, passphrase string) error { + devPath := types.GetVolumeDevicePath(volume, true) + if isOpen, err := IsDeviceOpen(devPath); err != nil { + return err + } else if !isOpen { + return fmt.Errorf("volume %v encrypto device is closed for resizing", volume) + } + + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc} + nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces) + if err != nil { + return err + } + + _, err = nsexec.LuksResize(volume, passphrase, lhtypes.LuksTimeout) + return err +} + // IsDeviceOpen determines if encrypted device is already open. func IsDeviceOpen(device string) (bool, error) { _, mappedFile, err := DeviceEncryptionStatus(device) diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index aa57c160..aa769785 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -8,8 +8,6 @@ import ( "time" "github.com/google/fscrypt/filesystem" - lhexec "github.com/longhorn/go-common-libs/exec" - lhtypes "github.com/longhorn/go-common-libs/types" "github.com/longhorn/types/pkg/generated/smrpc" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -20,6 +18,10 @@ import ( "google.golang.org/protobuf/types/known/emptypb" "k8s.io/mount-utils" + lhexec "github.com/longhorn/go-common-libs/exec" + lhtypes "github.com/longhorn/go-common-libs/types" + + "github.com/longhorn/longhorn-share-manager/pkg/crypto" "github.com/longhorn/longhorn-share-manager/pkg/server" "github.com/longhorn/longhorn-share-manager/pkg/server/nfs" "github.com/longhorn/longhorn-share-manager/pkg/types" @@ -114,6 +116,68 @@ func (s *ShareManagerServer) FilesystemTrim(ctx context.Context, req *smrpc.File return &emptypb.Empty{}, nil } +func (s *ShareManagerServer) FilesystemResize(ctx context.Context, req *emptypb.Empty) (resp *emptypb.Empty, err error) { + s.Lock() + defer s.Unlock() + + vol := s.manager.GetVolume() + if vol.Name == "" { + s.logger.Warn("Volume name is missing") + return &emptypb.Empty{}, nil + } + + log := s.logger.WithField("volume", vol.Name) + + defer func() { + if err != nil { + log.WithError(err).Errorf("Failed to resize mounted filesystem on volume") + } + }() + + devicePath := types.GetVolumeDevicePath(vol.Name, vol.IsEncrypted()) + if !volume.CheckDeviceValid(devicePath) { + return &emptypb.Empty{}, grpcstatus.Errorf(grpccodes.FailedPrecondition, "volume %v is not valid", vol.Name) + } + + mountPath := types.GetMountPath(vol.Name) + log = log.WithField("filesystem", mountPath) + + _, err = filesystem.GetMount(mountPath) + if err != nil { + return &emptypb.Empty{}, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + + log.Infof("Resizing mounted volume") + + // TODO - should this go into volume.ResizeVolume so that the resize at server startup is encryption-aware? + if vol.IsEncrypted() { + diskFormat, err := volume.GetDiskFormat(devicePath) + if err != nil { + return &emptypb.Empty{}, grpcstatus.Errorf(grpccodes.Internal, "failed to determine disk format of volume %v: %v", vol.Name, err) + } + log.Infof("Device %v contains filesystem of format %v", devicePath, diskFormat) + + if diskFormat != "crypto_LUKS" { + return &emptypb.Empty{}, grpcstatus.Errorf(grpccodes.InvalidArgument, "unsupported disk encryption format %v", diskFormat) + } + + if err = crypto.ResizeEncryptoDevice(vol.Name, vol.Passphrase); err != nil { + return &emptypb.Empty{}, grpcstatus.Errorf(grpccodes.Internal, "failed to resize crypto device %v for volume %v node expansion: %v", devicePath, vol.Name, err) + } + } + + if resized, err := volume.ResizeVolume(devicePath, mountPath); err != nil { + log.WithError(err).Errorf("Failed to resize filesystem") + return &emptypb.Empty{}, grpcstatus.Error(grpccodes.Internal, err.Error()) + } else if resized { + log.Infof("Resized filesystem") + } else { + log.Infof("No resize needed for filesystem") + } + + return &emptypb.Empty{}, nil +} + func (s *ShareManagerServer) unexport(vol volume.Volume) error { exporter, err := nfs.NewExporter(configPath, types.ExportPath) if err != nil { diff --git a/pkg/server/share_manager.go b/pkg/server/share_manager.go index ba51fae9..c9993183 100644 --- a/pkg/server/share_manager.go +++ b/pkg/server/share_manager.go @@ -188,7 +188,7 @@ func (m *ShareManager) setupDevice(vol volume.Volume, devicePath string) (string } m.logger.Infof("Volume %v device %v contains filesystem of format %v", vol.Name, devicePath, diskFormat) - if vol.IsEncrypted() || diskFormat == "luks" { + if vol.IsEncrypted() || diskFormat == "crypto_LUKS" { if vol.Passphrase == "" { return "", fmt.Errorf("missing passphrase for encrypted volume %v", vol.Name) }