diff --git a/Makefile b/Makefile index 0549467..76bece4 100755 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ build-linux: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $(BINARY_NAME) -v ./cmd/ docker-build: - docker build $(BUILDARGS) -t ${IMG} -f Dockerfile . + docker build $(BUILDARGS) -t ${IMG} -f Dockerfile . --load docker-push: docker push ${IMG} diff --git a/pkg/driver/constants.go b/pkg/driver/constants.go index f37b8fe..0895d49 100644 --- a/pkg/driver/constants.go +++ b/pkg/driver/constants.go @@ -33,6 +33,10 @@ const ( ParameterNodeID = "node_id" // ParameterDeviceName is the device name parameter ParameterDeviceName = "device_name" + // ParameterReadOnly is the name of the parameter used to specify whether the volume should be mounted as read-only + ParameterReadOnly = "readOnly" + // ParameterMkfsOptions is the name of the parameter used to specify the options for the mkfs command + ParameterMkfsOptions = "mkfs_options" CSIDriverName = "csi.ironcore.dev" topologyKey = "topology." + CSIDriverName + "/zone" diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go index 39b6fdf..0d277cd 100755 --- a/pkg/driver/controller.go +++ b/pkg/driver/controller.go @@ -57,6 +57,11 @@ func (d *driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) fstype = FSTypeExt4 } + mkfsOptions, ok := params[ParameterMkfsOptions] + if !ok { + mkfsOptions = "" + } + volumeClass, ok := params[ParameterType] if !ok { return nil, status.Errorf(codes.Internal, "Required parameter %s is missing", ParameterType) @@ -136,6 +141,7 @@ func (d *driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) ParameterVolumePool: volumePoolName, ParameterCreationTime: time.Unix(volume.CreationTimestamp.Unix(), 0).String(), ParameterFSType: fstype, + ParameterMkfsOptions: mkfsOptions, }, ContentSource: req.GetVolumeContentSource(), AccessibleTopology: accessibleTopology, @@ -461,7 +467,7 @@ func validateDeviceName(volume *storagev1alpha1.Volume, machine *computev1alpha1 } } } - return "", fmt.Errorf("failed to get device name of volume %s name from machine %s", client.ObjectKeyFromObject(volume), client.ObjectKeyFromObject(machine)) + return "", fmt.Errorf("failed to get device name of volume %s from machine %s", client.ObjectKeyFromObject(volume), client.ObjectKeyFromObject(machine)) } func isValidVolumeCapabilities(volCaps []*csi.VolumeCapability) bool { diff --git a/pkg/driver/node.go b/pkg/driver/node.go index 53f725b..39965f5 100755 --- a/pkg/driver/node.go +++ b/pkg/driver/node.go @@ -9,6 +9,7 @@ import ( "io" "os" "path/filepath" + "strings" "github.com/container-storage-interface/spec/lib/go/csi" "golang.org/x/sys/unix" @@ -33,7 +34,8 @@ var ( func (d *driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) { klog.InfoS("Staging volume on node ", "Volume", req.GetVolumeId(), "StagingTargetPath", req.GetStagingTargetPath()) - fstype := req.GetVolumeContext()[ParameterFSType] + volumeContext := req.GetVolumeContext() + fstype := volumeContext[ParameterFSType] devicePath := req.PublishContext[ParameterDeviceName] klog.InfoS("Check if the device path exists") @@ -42,16 +44,10 @@ func (d *driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ // We will requeue here since the device path is not ready yet. return nil, status.Errorf(codes.Unavailable, "Device path %s does not exist: %v", devicePath, err) } else { - return nil, status.Errorf(codes.Internal, "Failed to determine wether the device path %s exists: %v", devicePath, err) + return nil, status.Errorf(codes.Internal, "Failed to determine whether the device path %s exists: %v", devicePath, err) } } - readOnly := false - if req.GetVolumeContext()["readOnly"] == "true" { - readOnly = true - } - mountOptions := req.GetVolumeCapability().GetMount().GetMountFlags() - targetPath := req.GetStagingTargetPath() klog.InfoS("Validate mount point", "MountPoint", targetPath) notMnt, err := d.mounter.IsLikelyNotMountPoint(targetPath) @@ -68,16 +64,27 @@ func (d *driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ return nil, status.Errorf(codes.Internal, "Failed to create target directory %s for volume %s: %v", targetPath, req.GetVolumeId(), err) } - var options []string + mountOptions := req.GetVolumeCapability().GetMount().GetMountFlags() - if readOnly { + readOnly, ok := volumeContext[ParameterReadOnly] + readOnlyFlag := ok && readOnly == "true" + + var options []string + if readOnlyFlag { options = append(options, "ro") } else { options = append(options, "rw") } options = append(options, mountOptions...) + + var formatOptions []string + mkfsOptions, ok := volumeContext[ParameterMkfsOptions] + if ok && mkfsOptions != "" { + formatOptions = append(formatOptions, strings.Split(mkfsOptions, " ")...) + } + klog.InfoS("Format and mount the volume") - if err = d.mounter.FormatAndMount(devicePath, targetPath, fstype, options); err != nil { + if err = d.mounter.FormatAndMountSensitiveWithFormatOptions(devicePath, targetPath, fstype, options, nil, formatOptions); err != nil { return nil, status.Errorf(codes.Internal, "Failed to mount volume %s [%s] to %s: %v", devicePath, fstype, targetPath, err) } klog.InfoS("Staged volume on node", "Volume", req.GetVolumeId()) diff --git a/pkg/driver/node_test.go b/pkg/driver/node_test.go index 51efed4..0badeff 100644 --- a/pkg/driver/node_test.go +++ b/pkg/driver/node_test.go @@ -114,7 +114,7 @@ var _ = Describe("Node", func() { It("should fail if the mount operation fails", func(ctx SpecContext) { mockMounter.EXPECT().IsLikelyNotMountPoint(targetPath).Return(true, nil) mockOS.EXPECT().MkdirAll(targetPath, os.FileMode(0750)).Return(nil) - mockMounter.EXPECT().FormatAndMount(devicePath, targetPath, fstype, mountOptions).Return(errors.New("failed to mount volume")) + mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(devicePath, targetPath, fstype, mountOptions, nil, nil).Return(errors.New("failed to mount volume")) _, err := drv.NodeStageVolume(ctx, req) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Failed to mount volume")) @@ -126,7 +126,7 @@ var _ = Describe("Node", func() { It("should stage the volume", func(ctx SpecContext) { mockMounter.EXPECT().IsLikelyNotMountPoint(targetPath).Return(true, nil) mockOS.EXPECT().MkdirAll(targetPath, os.FileMode(0750)).Return(nil) - mockMounter.EXPECT().FormatAndMount(devicePath, targetPath, fstype, mountOptions).Return(nil) + mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(devicePath, targetPath, fstype, mountOptions, nil, nil).Return(nil) _, err := drv.NodeStageVolume(ctx, req) Expect(err).NotTo(HaveOccurred()) }) diff --git a/pkg/utils/mount/mock_mountutils_unix.go b/pkg/utils/mount/mock_mountutils_unix.go index c23fbb6..d2e0b50 100644 --- a/pkg/utils/mount/mock_mountutils_unix.go +++ b/pkg/utils/mount/mock_mountutils_unix.go @@ -58,18 +58,18 @@ func (mr *MockMountWrapperMockRecorder) CanSafelySkipMountPointCheck() *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanSafelySkipMountPointCheck", reflect.TypeOf((*MockMountWrapper)(nil).CanSafelySkipMountPointCheck)) } -// FormatAndMount mocks base method. -func (m *MockMountWrapper) FormatAndMount(source, target, fstype string, options []string) error { +// FormatAndMountSensitiveWithFormatOptions mocks base method. +func (m *MockMountWrapper) FormatAndMountSensitiveWithFormatOptions(source, target, fstype string, options, sensitiveOptions, formatOptions []string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FormatAndMount", source, target, fstype, options) + ret := m.ctrl.Call(m, "FormatAndMountSensitiveWithFormatOptions", source, target, fstype, options, sensitiveOptions, formatOptions) ret0, _ := ret[0].(error) return ret0 } -// FormatAndMount indicates an expected call of FormatAndMount. -func (mr *MockMountWrapperMockRecorder) FormatAndMount(source, target, fstype, options any) *gomock.Call { +// FormatAndMountSensitiveWithFormatOptions indicates an expected call of FormatAndMountSensitiveWithFormatOptions. +func (mr *MockMountWrapperMockRecorder) FormatAndMountSensitiveWithFormatOptions(source, target, fstype, options, sensitiveOptions, formatOptions any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FormatAndMount", reflect.TypeOf((*MockMountWrapper)(nil).FormatAndMount), source, target, fstype, options) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FormatAndMountSensitiveWithFormatOptions", reflect.TypeOf((*MockMountWrapper)(nil).FormatAndMountSensitiveWithFormatOptions), source, target, fstype, options, sensitiveOptions, formatOptions) } // GetMountRefs mocks base method. diff --git a/pkg/utils/mount/mountutils_unix.go b/pkg/utils/mount/mountutils_unix.go index 4582909..c0c3f51 100644 --- a/pkg/utils/mount/mountutils_unix.go +++ b/pkg/utils/mount/mountutils_unix.go @@ -18,7 +18,7 @@ import ( // SafeFormatAndMount). Defined it explicitly so that it can be mocked. type MountWrapper interface { k8smountutils.Interface - FormatAndMount(source string, target string, fstype string, options []string) error + FormatAndMountSensitiveWithFormatOptions(source string, target string, fstype string, options []string, sensitiveOptions []string, formatOptions []string) error NewResizeFs() (Resizefs, error) }