Skip to content

Commit

Permalink
Implement Cloning for csi-hostpath driver
Browse files Browse the repository at this point in the history
This PR adds support for cloning of hostpath volumes.
  • Loading branch information
j-griffith committed Jun 13, 2019
1 parent f4f3b14 commit 959c467
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 0 deletions.
15 changes: 15 additions & 0 deletions examples/csi-clone.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: hp-pvc-clone
spec:
storageClassName: csi-hostpath-sc
dataSource:
name: src-hp-pvc
kind: PersistentVolumeClaim
apiGroup: ""
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
29 changes: 29 additions & 0 deletions pkg/hostpath/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func NewControllerServer(ephemeral bool) *controllerServer {
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
}),
}
}
Expand Down Expand Up @@ -178,19 +179,47 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
snapshotId := contentSource.GetSnapshot().GetSnapshotId()
snapshot, ok := hostPathVolumeSnapshots[snapshotId]
if !ok {
deleteHostpathVolume(volumeID)
return nil, status.Errorf(codes.NotFound, "cannot find snapshot %v", snapshotId)
}
if snapshot.ReadyToUse != true {
deleteHostpathVolume(volumeID)
return nil, status.Errorf(codes.Internal, "Snapshot %v is not yet ready to use.", snapshotId)
}
snapshotPath := snapshot.Path
args := []string{"zxvf", snapshotPath, "-C", path}
executor := utilexec.New()
out, err := executor.Command("tar", args...).CombinedOutput()
if err != nil {
deleteHostpathVolume(volumeID)
return nil, status.Error(codes.Internal, fmt.Sprintf("failed pre-populate data for volume: %v: %s", err, out))
}
}
if srcVolume := contentSource.GetVolume(); srcVolume != nil {
srcVolumeID := srcVolume.GetVolumeId()
hostPathVolume, ok := hostPathVolumes[srcVolumeID]
if !ok {
deleteHostpathVolume(volumeID)
return nil, status.Error(codes.NotFound, "source volumeID does not exist, are source/destination in the same storage class?")
}
srcPath := hostPathVolume.VolPath
isEmpty, err := hostPathIsEmpty(srcPath)
if err != nil {
deleteHostpathVolume(volumeID)
return nil, status.Error(codes.Internal, fmt.Sprintf("failed verification check of source hostpath volume: %s: %v", srcVolumeID, err))
}

// If the source hostpath volume is empty it's a noop and we just move along, otherwise the cp call will fail with a a file stat error DNE
if !isEmpty {
args := []string{"-a", srcPath + "/*", path + "/"}
executor := utilexec.New()
out, err := executor.Command("cp", args...).CombinedOutput()
if err != nil {
deleteHostpathVolume(volumeID)
return nil, status.Error(codes.Internal, fmt.Sprintf("failed pre-populate data (clone) for volume: %s: %s", volumeID, out))
}
}
}
}

createVolumeResponse := &csi.CreateVolumeResponse{}
Expand Down
18 changes: 18 additions & 0 deletions pkg/hostpath/hostpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package hostpath

import (
"fmt"
"io"
"os"

"github.com/golang/glog"
Expand Down Expand Up @@ -169,10 +170,27 @@ func createHostpathVolume(volID, name string, cap int64, volAccessType accessTyp

// deleteVolume deletes the directory for the hostpath volume.
func deleteHostpathVolume(volID string) error {
glog.V(4).Infof("deleting hostpath volume: %s", volID)
path := getVolumePath(volID)
if err := os.RemoveAll(path); err != nil {
return err
}
delete(hostPathVolumes, volID)
return nil
}

// hostPathIsEmpty is a simple check to determine if the specified hostpath directory
// is empty or not.
func hostPathIsEmpty(p string) (bool, error) {
f, err := os.Open(p)
if err != nil {
return true, fmt.Errorf("unable to open hostpath volume, error: %v", err)
}
defer f.Close()

_, err = f.Readdir(1)
if err == io.EOF {
return true, nil
}
return false, err
}

0 comments on commit 959c467

Please sign in to comment.