Merge pull request #426 from wozniakjan/volume_to_volume_copy
feat: volume clone from source volume
This commit is contained in:
commit
cd50d48b7f
16
deploy/example/pvc-volume-clone.yaml
Normal file
16
deploy/example/pvc-volume-clone.yaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: pvc-nfs-clone
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
storageClassName: nfs-csi
|
||||||
|
dataSource:
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
name: pvc-nfs-dynamic
|
||||||
@ -19,6 +19,7 @@ package nfs
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -143,12 +144,19 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.GetVolumeContentSource() != nil {
|
||||||
|
if err := cs.copyVolume(ctx, req, nfsVol); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setKeyValueInMap(parameters, paramSubDir, nfsVol.subDir)
|
setKeyValueInMap(parameters, paramSubDir, nfsVol.subDir)
|
||||||
return &csi.CreateVolumeResponse{
|
return &csi.CreateVolumeResponse{
|
||||||
Volume: &csi.Volume{
|
Volume: &csi.Volume{
|
||||||
VolumeId: nfsVol.id,
|
VolumeId: nfsVol.id,
|
||||||
CapacityBytes: 0, // by setting it to zero, Provisioner will use PVC requested size as PV size
|
CapacityBytes: 0, // by setting it to zero, Provisioner will use PVC requested size as PV size
|
||||||
VolumeContext: parameters,
|
VolumeContext: parameters,
|
||||||
|
ContentSource: req.GetVolumeContentSource(),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -307,6 +315,58 @@ func (cs *ControllerServer) internalUnmount(ctx context.Context, vol *nfsVolume)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cs *ControllerServer) copyFromVolume(ctx context.Context, req *csi.CreateVolumeRequest, dstVol *nfsVolume) error {
|
||||||
|
srcVol, err := getNfsVolFromID(req.GetVolumeContentSource().GetVolume().GetVolumeId())
|
||||||
|
if err != nil {
|
||||||
|
return status.Error(codes.NotFound, err.Error())
|
||||||
|
}
|
||||||
|
// Note that the source path must include trailing '/.', can't use 'filepath.Join()' as it performs path cleaning
|
||||||
|
srcPath := fmt.Sprintf("%v/.", getInternalVolumePath(cs.Driver.workingMountDir, srcVol))
|
||||||
|
dstPath := getInternalVolumePath(cs.Driver.workingMountDir, dstVol)
|
||||||
|
klog.V(2).Infof("copy volume from volume %v -> %v", srcPath, dstPath)
|
||||||
|
|
||||||
|
var volCap *csi.VolumeCapability
|
||||||
|
if len(req.GetVolumeCapabilities()) > 0 {
|
||||||
|
volCap = req.GetVolumeCapabilities()[0]
|
||||||
|
}
|
||||||
|
if err = cs.internalMount(ctx, srcVol, nil, volCap); err != nil {
|
||||||
|
return status.Errorf(codes.Internal, "failed to mount src nfs server: %v", err.Error())
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err = cs.internalUnmount(ctx, srcVol); err != nil {
|
||||||
|
klog.Warningf("failed to unmount nfs server: %v", err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err = cs.internalMount(ctx, dstVol, nil, volCap); err != nil {
|
||||||
|
return status.Errorf(codes.Internal, "failed to mount dst nfs server: %v", err.Error())
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err = cs.internalUnmount(ctx, dstVol); err != nil {
|
||||||
|
klog.Warningf("failed to unmount dst nfs server: %v", err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// recursive 'cp' with '-a' to handle symlinks
|
||||||
|
out, err := exec.Command("cp", "-a", srcPath, dstPath).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return status.Error(codes.Internal, fmt.Sprintf("%v: %v", err, string(out)))
|
||||||
|
}
|
||||||
|
klog.V(2).Infof("copied %s -> %s", srcPath, dstPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ControllerServer) copyVolume(ctx context.Context, req *csi.CreateVolumeRequest, vol *nfsVolume) error {
|
||||||
|
vs := req.VolumeContentSource
|
||||||
|
switch vs.Type.(type) {
|
||||||
|
case *csi.VolumeContentSource_Snapshot:
|
||||||
|
return status.Error(codes.Unimplemented, "Currently only volume copy from another volume is supported")
|
||||||
|
case *csi.VolumeContentSource_Volume:
|
||||||
|
return cs.copyFromVolume(ctx, req, vol)
|
||||||
|
default:
|
||||||
|
return status.Errorf(codes.InvalidArgument, "%v not a proper volume source", vs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// newNFSVolume Convert VolumeCreate parameters to an nfsVolume
|
// newNFSVolume Convert VolumeCreate parameters to an nfsVolume
|
||||||
func newNFSVolume(name string, size int64, params map[string]string) (*nfsVolume, error) {
|
func newNFSVolume(name string, size int64, params map[string]string) (*nfsVolume, error) {
|
||||||
var server, baseDir, subDir string
|
var server, baseDir, subDir string
|
||||||
|
|||||||
@ -325,6 +325,13 @@ func TestControllerGetCapabilities(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Type: &csi.ControllerServiceCapability_Rpc{
|
||||||
|
Rpc: &csi.ControllerServiceCapability_RPC{
|
||||||
|
Type: csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
|
|||||||
@ -84,6 +84,7 @@ func NewDriver(options *DriverOptions) *Driver {
|
|||||||
n.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{
|
n.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{
|
||||||
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
|
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
|
||||||
csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
|
csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
|
||||||
|
csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
|
||||||
})
|
})
|
||||||
|
|
||||||
n.AddNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{
|
n.AddNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{
|
||||||
|
|||||||
@ -12,6 +12,7 @@ DriverInfo:
|
|||||||
multipods: true
|
multipods: true
|
||||||
RWX: true
|
RWX: true
|
||||||
fsGroup: true
|
fsGroup: true
|
||||||
|
pvcDataSource: true
|
||||||
InlineVolumes:
|
InlineVolumes:
|
||||||
- Attributes:
|
- Attributes:
|
||||||
server: nfs-server.default.svc.cluster.local
|
server: nfs-server.default.svc.cluster.local
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user