Skip to content

Commit 0bb7116

Browse files
committed
Refactor: Move block, snapshot and cloning logic into separate functions
1 parent 82936aa commit 0bb7116

File tree

2 files changed

+122
-121
lines changed

2 files changed

+122
-121
lines changed

pkg/hostpath/controllerserver.go

Lines changed: 26 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import (
3333
"google.golang.org/grpc/status"
3434

3535
"github.com/container-storage-interface/spec/lib/go/csi"
36-
"k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
3736
utilexec "k8s.io/utils/exec"
3837
)
3938

@@ -138,114 +137,46 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
138137
},
139138
}, nil
140139
}
141-
return nil, status.Error(codes.AlreadyExists, fmt.Sprintf("Volume with the same name: %s but with different size already exist", req.GetName()))
140+
return nil, status.Errorf(codes.AlreadyExists, "Volume with the same name: %s but with different size already exist", req.GetName())
142141
}
143142

144143
volumeID := uuid.NewUUID().String()
145144
path := getVolumePath(volumeID)
146145

147-
if requestedAccessType == blockAccess {
148-
executor := utilexec.New()
149-
size := fmt.Sprintf("%dM", capacity/mib)
150-
// Create a block file.
151-
out, err := executor.Command("fallocate", "-l", size, path).CombinedOutput()
152-
if err != nil {
153-
glog.V(3).Infof("failed to create block device: %v", string(out))
154-
return nil, err
155-
}
156-
157-
// Associate block file with the loop device.
158-
volPathHandler := volumepathhandler.VolumePathHandler{}
159-
_, err = volPathHandler.AttachFileDevice(path)
160-
if err != nil {
161-
glog.Errorf("failed to attach device: %v", err)
162-
// Remove the block file because it'll no longer be used again.
163-
if err2 := os.Remove(path); err != nil {
164-
glog.Errorf("failed to cleanup block file %s: %v", path, err2)
165-
}
166-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to attach device: %v", err))
167-
}
168-
}
169-
170146
vol, err := createHostpathVolume(volumeID, req.GetName(), capacity, requestedAccessType, false /* ephemeral */)
171147
if err != nil {
172-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create volume: %s", err))
148+
return nil, status.Errorf(codes.Internal, "failed to create volume %v: %v", volumeID, err)
173149
}
174150
glog.V(4).Infof("created volume %s at path %s", vol.VolID, vol.VolPath)
175151

176152
if req.GetVolumeContentSource() != nil {
177153
contentSource := req.GetVolumeContentSource()
178-
if contentSource.GetSnapshot() != nil {
179-
snapshotId := contentSource.GetSnapshot().GetSnapshotId()
180-
snapshot, ok := hostPathVolumeSnapshots[snapshotId]
181-
if !ok {
182-
deleteHostpathVolume(volumeID)
183-
return nil, status.Errorf(codes.NotFound, "cannot find snapshot %v", snapshotId)
184-
}
185-
if snapshot.ReadyToUse != true {
186-
deleteHostpathVolume(volumeID)
187-
return nil, status.Errorf(codes.Internal, "Snapshot %v is not yet ready to use.", snapshotId)
188-
}
189-
snapshotPath := snapshot.Path
190-
args := []string{"zxvf", snapshotPath, "-C", path}
191-
executor := utilexec.New()
192-
out, err := executor.Command("tar", args...).CombinedOutput()
193-
if err != nil {
194-
deleteHostpathVolume(volumeID)
195-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed pre-populate data for volume: %v: %s", err, out))
196-
}
154+
if snapshot := contentSource.GetSnapshot(); snapshot != nil {
155+
err = loadFromSnapshot(snapshot.GetSnapshotId(), path)
197156
}
198157
if srcVolume := contentSource.GetVolume(); srcVolume != nil {
199-
srcVolumeID := srcVolume.GetVolumeId()
200-
hostPathVolume, ok := hostPathVolumes[srcVolumeID]
201-
if !ok {
202-
deleteHostpathVolume(volumeID)
203-
return nil, status.Error(codes.NotFound, "source volumeID does not exist, are source/destination in the same storage class?")
204-
}
205-
srcPath := hostPathVolume.VolPath
206-
isEmpty, err := hostPathIsEmpty(srcPath)
207-
if err != nil {
208-
deleteHostpathVolume(volumeID)
209-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed verification check of source hostpath volume: %s: %v", srcVolumeID, err))
210-
}
211-
212-
// 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
213-
if !isEmpty {
214-
args := []string{"-a", srcPath + "/*", path + "/"}
215-
executor := utilexec.New()
216-
out, err := executor.Command("cp", args...).CombinedOutput()
217-
if err != nil {
218-
deleteHostpathVolume(volumeID)
219-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed pre-populate data (clone) for volume: %s: %s", volumeID, out))
220-
}
158+
err = loadFromVolume(srcVolume.GetVolumeId(), path)
159+
}
160+
if err != nil {
161+
if delErr := deleteHostpathVolume(volumeID); delErr != nil {
162+
glog.V(2).Infof("deleting hostpath volume %v failed: %v", volumeID, delErr)
221163
}
164+
return nil, err
222165
}
166+
glog.V(4).Infof("successfully populated volume %s", vol.VolID)
223167
}
224168

225-
createVolumeResponse := &csi.CreateVolumeResponse{}
226-
if req.GetVolumeContentSource() != nil {
227-
createVolumeResponse = &csi.CreateVolumeResponse{
228-
Volume: &csi.Volume{
229-
VolumeId: volumeID,
230-
CapacityBytes: req.GetCapacityRange().GetRequiredBytes(),
231-
VolumeContext: req.GetParameters(),
232-
ContentSource: req.GetVolumeContentSource(),
233-
},
234-
}
235-
} else {
236-
createVolumeResponse = &csi.CreateVolumeResponse{
237-
Volume: &csi.Volume{
238-
VolumeId: volumeID,
239-
CapacityBytes: req.GetCapacityRange().GetRequiredBytes(),
240-
VolumeContext: req.GetParameters(),
241-
},
242-
}
243-
}
244-
return createVolumeResponse, nil
169+
return &csi.CreateVolumeResponse{
170+
Volume: &csi.Volume{
171+
VolumeId: volumeID,
172+
CapacityBytes: req.GetCapacityRange().GetRequiredBytes(),
173+
VolumeContext: req.GetParameters(),
174+
ContentSource: req.GetVolumeContentSource(),
175+
},
176+
}, nil
245177
}
246178

247179
func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
248-
249180
// Check arguments
250181
if len(req.GetVolumeId()) == 0 {
251182
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
@@ -256,36 +187,12 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
256187
return nil, err
257188
}
258189

259-
vol, err := getVolumeByID(req.GetVolumeId())
260-
if err != nil {
261-
// Return OK if the volume is not found.
262-
return &csi.DeleteVolumeResponse{}, nil
263-
}
264-
glog.V(4).Infof("deleting volume %s", vol.VolID)
265-
266-
if vol.VolAccessType == blockAccess {
267-
268-
volPathHandler := volumepathhandler.VolumePathHandler{}
269-
// Get the associated loop device.
270-
device, err := volPathHandler.GetLoopDevice(getVolumePath(vol.VolID))
271-
if err != nil {
272-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to get the loop device: %v", err))
273-
}
274-
275-
if device != "" {
276-
// Remove any associated loop device.
277-
glog.V(4).Infof("deleting loop device %s", device)
278-
if err := volPathHandler.RemoveLoopDevice(device); err != nil {
279-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to remove loop device: %v", err))
280-
}
281-
}
282-
}
283-
284-
if err := deleteHostpathVolume(vol.VolID); err != nil && !os.IsNotExist(err) {
285-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to delete volume: %s", err))
190+
volId := req.GetVolumeId()
191+
if err := deleteHostpathVolume(volId); err != nil {
192+
return nil, status.Errorf(codes.Internal, "failed to delete volume %v: %v", volId, err)
286193
}
287194

288-
glog.V(4).Infof("volume deleted ok: %s", vol.VolID)
195+
glog.V(4).Infof("volume %v successfully deleted", volId)
289196

290197
return &csi.DeleteVolumeResponse{}, nil
291198
}
@@ -377,7 +284,7 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
377284
},
378285
}, nil
379286
}
380-
return nil, status.Error(codes.AlreadyExists, fmt.Sprintf("snapshot with the same name: %s but with different SourceVolumeId already exist", req.GetName()))
287+
return nil, status.Errorf(codes.AlreadyExists, "snapshot with the same name: %s but with different SourceVolumeId already exist", req.GetName())
381288
}
382289

383290
volumeID := req.GetSourceVolumeId()
@@ -402,7 +309,7 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
402309
executor := utilexec.New()
403310
out, err := executor.Command("tar", args...).CombinedOutput()
404311
if err != nil {
405-
return nil, status.Error(codes.Internal, fmt.Sprintf("failed create snapshot: %v: %s", err, out))
312+
return nil, status.Errorf(codes.Internal, "failed create snapshot: %v: %s", err, out)
406313
}
407314

408315
glog.V(4).Infof("create volume snapshot %s", file)
@@ -579,7 +486,7 @@ func (cs *controllerServer) validateControllerServiceRequest(c csi.ControllerSer
579486
return nil
580487
}
581488
}
582-
return status.Error(codes.InvalidArgument, fmt.Sprintf("%s", c))
489+
return status.Errorf(codes.InvalidArgument, "unsupported capability %s", c)
583490
}
584491

585492
func getControllerServiceCapabilities(cl []csi.ControllerServiceCapability_RPC_Type) []*csi.ControllerServiceCapability {

pkg/hostpath/hostpath.go

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import (
2222
"os"
2323

2424
"github.com/golang/glog"
25+
"google.golang.org/grpc/codes"
26+
"google.golang.org/grpc/status"
27+
"k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
28+
utilexec "k8s.io/utils/exec"
2529

2630
timestamp "github.com/golang/protobuf/ptypes/timestamp"
2731
)
@@ -151,11 +155,34 @@ func getVolumePath(volID string) string {
151155
// It returns the volume path or err if one occurs.
152156
func createHostpathVolume(volID, name string, cap int64, volAccessType accessType, ephemeral bool) (*hostPathVolume, error) {
153157
path := getVolumePath(volID)
154-
if volAccessType == mountAccess {
158+
159+
switch volAccessType {
160+
case mountAccess:
155161
err := os.MkdirAll(path, 0777)
156162
if err != nil {
157163
return nil, err
158164
}
165+
case blockAccess:
166+
executor := utilexec.New()
167+
size := fmt.Sprintf("%dM", cap/mib)
168+
// Create a block file.
169+
out, err := executor.Command("fallocate", "-l", size, path).CombinedOutput()
170+
if err != nil {
171+
return nil, fmt.Errorf("failed to create block device: %v, %v", err, string(out))
172+
}
173+
174+
// Associate block file with the loop device.
175+
volPathHandler := volumepathhandler.VolumePathHandler{}
176+
_, err = volPathHandler.AttachFileDevice(path)
177+
if err != nil {
178+
// Remove the block file because it'll no longer be used again.
179+
if err2 := os.Remove(path); err2 != nil {
180+
glog.Errorf("failed to cleanup block file %s: %v", path, err2)
181+
}
182+
return nil, fmt.Errorf("failed to attach device %v: %v", path, err)
183+
}
184+
default:
185+
return nil, fmt.Errorf("unsupported access type %v", volAccessType)
159186
}
160187

161188
hostpathVol := hostPathVolume{
@@ -173,8 +200,32 @@ func createHostpathVolume(volID, name string, cap int64, volAccessType accessTyp
173200
// deleteVolume deletes the directory for the hostpath volume.
174201
func deleteHostpathVolume(volID string) error {
175202
glog.V(4).Infof("deleting hostpath volume: %s", volID)
203+
204+
vol, err := getVolumeByID(volID)
205+
if err != nil {
206+
// Return OK if the volume is not found.
207+
return nil
208+
}
209+
210+
if vol.VolAccessType == blockAccess {
211+
volPathHandler := volumepathhandler.VolumePathHandler{}
212+
// Get the associated loop device.
213+
device, err := volPathHandler.GetLoopDevice(getVolumePath(volID))
214+
if err != nil {
215+
return fmt.Errorf("failed to get the loop device: %v", err)
216+
}
217+
218+
if device != "" {
219+
// Remove any associated loop device.
220+
glog.V(4).Infof("deleting loop device %s", device)
221+
if err := volPathHandler.RemoveLoopDevice(device); err != nil {
222+
return fmt.Errorf("failed to remove loop device %v: %v", device, err)
223+
}
224+
}
225+
}
226+
176227
path := getVolumePath(volID)
177-
if err := os.RemoveAll(path); err != nil {
228+
if err := os.RemoveAll(path); err != nil && !os.IsNotExist(err) {
178229
return err
179230
}
180231
delete(hostPathVolumes, volID)
@@ -196,3 +247,46 @@ func hostPathIsEmpty(p string) (bool, error) {
196247
}
197248
return false, err
198249
}
250+
251+
// loadFromSnapshot populates the given destPath with data from the snapshotID
252+
func loadFromSnapshot(snapshotId, destPath string) error {
253+
snapshot, ok := hostPathVolumeSnapshots[snapshotId]
254+
if !ok {
255+
return status.Errorf(codes.NotFound, "cannot find snapshot %v", snapshotId)
256+
}
257+
if snapshot.ReadyToUse != true {
258+
return status.Errorf(codes.Internal, "snapshot %v is not yet ready to use.", snapshotId)
259+
}
260+
snapshotPath := snapshot.Path
261+
args := []string{"zxvf", snapshotPath, "-C", destPath}
262+
executor := utilexec.New()
263+
out, err := executor.Command("tar", args...).CombinedOutput()
264+
if err != nil {
265+
return status.Errorf(codes.Internal, "failed pre-populate data from snapshot %v: %v: %s", snapshotId, err, out)
266+
}
267+
return nil
268+
}
269+
270+
// loadfromVolume populates the given destPath with data from the srcVolumeID
271+
func loadFromVolume(srcVolumeId, destPath string) error {
272+
hostPathVolume, ok := hostPathVolumes[srcVolumeId]
273+
if !ok {
274+
return status.Error(codes.NotFound, "source volumeId does not exist, are source/destination in the same storage class?")
275+
}
276+
srcPath := hostPathVolume.VolPath
277+
isEmpty, err := hostPathIsEmpty(srcPath)
278+
if err != nil {
279+
return status.Errorf(codes.Internal, "failed verification check of source hostpath volume: %s: %v", srcVolumeId, err)
280+
}
281+
282+
// 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
283+
if !isEmpty {
284+
args := []string{"-a", srcPath + "/*", destPath + "/"}
285+
executor := utilexec.New()
286+
out, err := executor.Command("cp", args...).CombinedOutput()
287+
if err != nil {
288+
return status.Errorf(codes.Internal, "failed pre-populate data from volume %v: %v: %s", srcVolumeId, err, out)
289+
}
290+
}
291+
return nil
292+
}

0 commit comments

Comments
 (0)