You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
8.9 KiB
Go

/*
Copyright 2023.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package webdav
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/container-storage-interface/spec/lib/go/csi"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/klog/v2"
"git.ruekov.eu/ruakij/webdav-csi-driver/pkg/webdav/mount"
)
type ControllerServer struct {
csi.UnimplementedControllerServer
*Driver
mounter mount.Interface
}
func NewControllerServer(d *Driver, mounter mount.Interface) *ControllerServer {
return &ControllerServer{
Driver: d,
mounter: mounter,
}
}
// CreateVolume implements csi.ControllerServer.
func (c *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
name := req.GetName()
if len(name) == 0 {
return nil, status.Error(codes.InvalidArgument, "CreateVolume name must be provided")
}
if err := isValidVolumeCapabilities(req.GetVolumeCapabilities()); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
mountPermissions := c.Driver.mountPermissions
parameters := req.GetParameters()
if parameters == nil {
parameters = make(map[string]string)
}
for k, v := range parameters {
switch strings.ToLower(k) {
case webdavSharePath, pvcNameKey, pvcNamespaceKey, pvNameKey:
case mountPermissionsField:
if v != "" {
var err error
if mountPermissions, err = strconv.ParseUint(v, 8, 32); err != nil {
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid mountPermissions %s in storage class", v))
}
}
default:
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid parameter %q in storage class", k))
}
}
targetPath := c.workingMountDir
sourcePath := req.Parameters[webdavSharePath]
notMnt, err := c.mounter.IsLikelyNotMountPoint(targetPath)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(targetPath, 0750); err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
notMnt = true
} else {
return nil, status.Error(codes.Internal, err.Error())
}
}
if !notMnt {
return nil, status.Errorf(codes.Internal, fmt.Sprintf("target path %s is alredy mounted", targetPath))
}
stdin := []string{req.GetSecrets()[secretUsernameKey], req.GetSecrets()[secretPasswordKey]}
if err := c.mounter.MountSensitiveWithStdin(sourcePath, targetPath, fstype, nil, nil, stdin); err != nil {
return nil, status.Errorf(codes.Internal, fmt.Sprintf("mount failed: %v", err.Error()))
}
internalVolumePath := filepath.Join(targetPath, req.Name)
if err = os.Mkdir(internalVolumePath, 0777); err != nil && !os.IsExist(err) {
return nil, status.Errorf(codes.Internal, "failed to make subdirectory: %v", err.Error())
}
defer func() {
if err = c.mounter.Unmount(targetPath); err != nil {
klog.Warningf("failed to unmount targetpath %s: %v", targetPath, err.Error())
}
}()
if mountPermissions > 0 {
// Reset directory permissions because of umask problems
if err = os.Chmod(internalVolumePath, os.FileMode(mountPermissions)); err != nil {
klog.Warningf("failed to chmod subdirectory: %v", err.Error())
}
}
return &csi.CreateVolumeResponse{
Volume: &csi.Volume{
VolumeId: MakeVolumeId(sourcePath, req.Name),
CapacityBytes: 0, // by setting it to zero, Provisioner will use PVC requested size as PV size
VolumeContext: nil,
ContentSource: req.GetVolumeContentSource(),
},
}, nil
}
// DeleteVolume implements csi.ControllerServer.
func (c *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
volumeID := req.GetVolumeId()
if volumeID == "" {
return nil, status.Error(codes.InvalidArgument, "volume id is empty")
}
sourcePath, subDir, err := ParseVolumeId(volumeID)
if err != nil {
// An invalid ID should be treated as doesn't exist
klog.Warningf("failed to parse volume for volume id %v deletion: %v", volumeID, err)
return &csi.DeleteVolumeResponse{}, nil
}
stdin := []string{req.GetSecrets()[secretUsernameKey], req.GetSecrets()[secretPasswordKey]}
targetPath := c.workingMountDir
if err := c.mounter.MountSensitiveWithStdin(sourcePath, targetPath, fstype, nil, nil, stdin); err != nil {
return nil, status.Errorf(codes.Internal, fmt.Sprintf("mount failed: %v", err.Error()))
}
defer func() {
if err = c.mounter.Unmount(targetPath); err != nil {
klog.Warningf("failed to unmount targetpath %s: %v", targetPath, err.Error())
}
}()
internalVolumePath := filepath.Join(targetPath, subDir)
klog.V(2).Infof("Removing subdirectory at %v", internalVolumePath)
if err = os.RemoveAll(internalVolumePath); err != nil {
return nil, status.Errorf(codes.Internal, "failed to delete subdirectory: %v", err.Error())
}
return &csi.DeleteVolumeResponse{}, nil
}
// ValidateVolumeCapabilities implements csi.ControllerServer.
func (c *ControllerServer) ValidateVolumeCapabilities(_ context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) {
if len(req.GetVolumeId()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
}
if err := isValidVolumeCapabilities(req.GetVolumeCapabilities()); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return &csi.ValidateVolumeCapabilitiesResponse{
Confirmed: &csi.ValidateVolumeCapabilitiesResponse_Confirmed{
VolumeCapabilities: req.GetVolumeCapabilities(),
},
Message: "",
}, nil
}
// ControllerGetCapabilities implements csi.ControllerServer.
func (c *ControllerServer) ControllerGetCapabilities(context.Context, *csi.ControllerGetCapabilitiesRequest) (*csi.ControllerGetCapabilitiesResponse, error) {
return &csi.ControllerGetCapabilitiesResponse{
Capabilities: c.Driver.cscap,
}, nil
}
// ControllerExpandVolume implements csi.ControllerServer.
func (*ControllerServer) ControllerExpandVolume(context.Context, *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// ControllerGetVolume implements csi.ControllerServer.
func (*ControllerServer) ControllerGetVolume(context.Context, *csi.ControllerGetVolumeRequest) (*csi.ControllerGetVolumeResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// ControllerModifyVolume implements csi.ControllerServer.
func (*ControllerServer) ControllerModifyVolume(context.Context, *csi.ControllerModifyVolumeRequest) (*csi.ControllerModifyVolumeResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// ControllerPublishVolume implements csi.ControllerServer.
func (*ControllerServer) ControllerPublishVolume(context.Context, *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// ControllerUnpublishVolume implements csi.ControllerServer.
func (*ControllerServer) ControllerUnpublishVolume(context.Context, *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// CreateSnapshot implements csi.ControllerServer.
func (*ControllerServer) CreateSnapshot(context.Context, *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// DeleteSnapshot implements csi.ControllerServer.
func (*ControllerServer) DeleteSnapshot(context.Context, *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// GetCapacity implements csi.ControllerServer.
func (*ControllerServer) GetCapacity(context.Context, *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// ListSnapshots implements csi.ControllerServer.
func (*ControllerServer) ListSnapshots(context.Context, *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// ListVolumes implements csi.ControllerServer.
func (*ControllerServer) ListVolumes(context.Context, *csi.ListVolumesRequest) (*csi.ListVolumesResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// isValidVolumeCapabilities validates the given VolumeCapability array is valid
func isValidVolumeCapabilities(volCaps []*csi.VolumeCapability) error {
if len(volCaps) == 0 {
return fmt.Errorf("volume capabilities missing in request")
}
for _, c := range volCaps {
if c.GetBlock() != nil {
return fmt.Errorf("block volume capability not supported")
}
}
return nil
}