created struct for keeping the state of a staging transaction

this way extending transaction rollbacks is easier

Signed-off-by: Reinier Schoof <reinier@skoef.nl>
This commit is contained in:
Reinier Schoof 2020-02-25 09:00:55 +01:00 committed by mergify[bot]
parent 9d7b50dccb
commit 3af5e0619f

View File

@ -45,6 +45,18 @@ type NodeServer struct {
VolumeLocks *util.VolumeLocks VolumeLocks *util.VolumeLocks
} }
// stageTransaction struct represents the state a transaction was when it either completed
// or failed
// this transaction state can be used to rollback the transaction
type stageTransaction struct {
// isStagePathCreated represents whether the mount path to stage the volume on was created or not
isStagePathCreated bool
// isMounted represents if the volume was mounted or not
isMounted bool
// isEncrypted represents if the volume was encrypted or not
isEncrypted bool
}
// NodeStageVolume mounts the volume to a staging path on the node. // NodeStageVolume mounts the volume to a staging path on the node.
// Implementation notes: // Implementation notes:
// - stagingTargetPath is the directory passed in the request where the volume needs to be staged // - stagingTargetPath is the directory passed in the request where the volume needs to be staged
@ -143,10 +155,7 @@ func (ns *NodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
} }
volOptions.VolID = volID volOptions.VolID = volID
transaction := stageTransaction{}
isMounted := false
isEncrypted := false
isStagePathCreated := false
devicePath := "" devicePath := ""
// Stash image details prior to mapping the image (useful during Unstage as it has no // Stash image details prior to mapping the image (useful during Unstage as it has no
@ -157,13 +166,13 @@ func (ns *NodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
} }
defer func() { defer func() {
if err != nil { if err != nil {
ns.undoStagingTransaction(ctx, req, devicePath, isStagePathCreated, isMounted, isEncrypted) ns.undoStagingTransaction(ctx, req, devicePath, transaction)
} }
}() }()
// perform the actual staging and if this fails, have undoStagingTransaction // perform the actual staging and if this fails, have undoStagingTransaction
// cleans up for us // cleans up for us
isStagePathCreated, isMounted, isEncrypted, err = ns.stageTransaction(ctx, req, volOptions, staticVol) transaction, err = ns.stageTransaction(ctx, req, volOptions, staticVol)
if err != nil { if err != nil {
return nil, status.Error(codes.Internal, err.Error()) return nil, status.Error(codes.Internal, err.Error())
} }
@ -173,17 +182,15 @@ func (ns *NodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
return &csi.NodeStageVolumeResponse{}, nil return &csi.NodeStageVolumeResponse{}, nil
} }
func (ns *NodeServer) stageTransaction(ctx context.Context, req *csi.NodeStageVolumeRequest, volOptions *rbdVolume, staticVol bool) (bool, bool, bool, error) { func (ns *NodeServer) stageTransaction(ctx context.Context, req *csi.NodeStageVolumeRequest, volOptions *rbdVolume, staticVol bool) (stageTransaction, error) {
isStagePathCreated := false transaction := stageTransaction{}
isMounted := false
isEncrypted := false
var err error var err error
var cr *util.Credentials var cr *util.Credentials
cr, err = util.NewUserCredentials(req.GetSecrets()) cr, err = util.NewUserCredentials(req.GetSecrets())
if err != nil { if err != nil {
return isStagePathCreated, isMounted, isEncrypted, err return transaction, err
} }
defer cr.DeleteCredentials() defer cr.DeleteCredentials()
@ -191,7 +198,7 @@ func (ns *NodeServer) stageTransaction(ctx context.Context, req *csi.NodeStageVo
var devicePath string var devicePath string
devicePath, err = attachRBDImage(ctx, volOptions, cr) devicePath, err = attachRBDImage(ctx, volOptions, cr)
if err != nil { if err != nil {
return isStagePathCreated, isMounted, isEncrypted, err return transaction, err
} }
klog.V(4).Infof(util.Log(ctx, "rbd image: %s/%s was successfully mapped at %s\n"), klog.V(4).Infof(util.Log(ctx, "rbd image: %s/%s was successfully mapped at %s\n"),
@ -200,9 +207,9 @@ func (ns *NodeServer) stageTransaction(ctx context.Context, req *csi.NodeStageVo
if volOptions.Encrypted { if volOptions.Encrypted {
devicePath, err = ns.processEncryptedDevice(ctx, volOptions, devicePath, cr) devicePath, err = ns.processEncryptedDevice(ctx, volOptions, devicePath, cr)
if err != nil { if err != nil {
return isStagePathCreated, isMounted, isEncrypted, err return transaction, err
} }
isEncrypted = true transaction.isEncrypted = true
} }
stagingTargetPath := getStagingTargetPath(req) stagingTargetPath := getStagingTargetPath(req)
@ -210,29 +217,29 @@ func (ns *NodeServer) stageTransaction(ctx context.Context, req *csi.NodeStageVo
isBlock := req.GetVolumeCapability().GetBlock() != nil isBlock := req.GetVolumeCapability().GetBlock() != nil
err = ns.createStageMountPoint(ctx, stagingTargetPath, isBlock) err = ns.createStageMountPoint(ctx, stagingTargetPath, isBlock)
if err != nil { if err != nil {
return isStagePathCreated, isMounted, isEncrypted, err return transaction, err
} }
isStagePathCreated = true transaction.isStagePathCreated = true
// nodeStage Path // nodeStage Path
err = ns.mountVolumeToStagePath(ctx, req, staticVol, stagingTargetPath, devicePath) err = ns.mountVolumeToStagePath(ctx, req, staticVol, stagingTargetPath, devicePath)
if err != nil { if err != nil {
return isStagePathCreated, isMounted, isEncrypted, err return transaction, err
} }
isMounted = true transaction.isMounted = true
// #nosec - allow anyone to write inside the target path // #nosec - allow anyone to write inside the target path
err = os.Chmod(stagingTargetPath, 0777) err = os.Chmod(stagingTargetPath, 0777)
return isStagePathCreated, isMounted, isEncrypted, err return transaction, err
} }
func (ns *NodeServer) undoStagingTransaction(ctx context.Context, req *csi.NodeStageVolumeRequest, devicePath string, isStagePathCreated, isMounted, isEncrypted bool) { func (ns *NodeServer) undoStagingTransaction(ctx context.Context, req *csi.NodeStageVolumeRequest, devicePath string, transaction stageTransaction) {
var err error var err error
stagingTargetPath := getStagingTargetPath(req) stagingTargetPath := getStagingTargetPath(req)
if isMounted { if transaction.isMounted {
err = ns.mounter.Unmount(stagingTargetPath) err = ns.mounter.Unmount(stagingTargetPath)
if err != nil { if err != nil {
klog.Errorf(util.Log(ctx, "failed to unmount stagingtargetPath: %s with error: %v"), stagingTargetPath, err) klog.Errorf(util.Log(ctx, "failed to unmount stagingtargetPath: %s with error: %v"), stagingTargetPath, err)
@ -241,7 +248,7 @@ func (ns *NodeServer) undoStagingTransaction(ctx context.Context, req *csi.NodeS
} }
// remove the file/directory created on staging path // remove the file/directory created on staging path
if isStagePathCreated { if transaction.isStagePathCreated {
err = os.Remove(stagingTargetPath) err = os.Remove(stagingTargetPath)
if err != nil { if err != nil {
klog.Errorf(util.Log(ctx, "failed to remove stagingtargetPath: %s with error: %v"), stagingTargetPath, err) klog.Errorf(util.Log(ctx, "failed to remove stagingtargetPath: %s with error: %v"), stagingTargetPath, err)
@ -253,7 +260,7 @@ func (ns *NodeServer) undoStagingTransaction(ctx context.Context, req *csi.NodeS
// Unmapping rbd device // Unmapping rbd device
if devicePath != "" { if devicePath != "" {
err = detachRBDDevice(ctx, devicePath, volID, isEncrypted) err = detachRBDDevice(ctx, devicePath, volID, transaction.isEncrypted)
if err != nil { if err != nil {
klog.Errorf(util.Log(ctx, "failed to unmap rbd device: %s for volume %s with error: %v"), devicePath, volID, err) klog.Errorf(util.Log(ctx, "failed to unmap rbd device: %s for volume %s with error: %v"), devicePath, volID, err)
// continue on failure to delete the stash file, as kubernetes will fail to delete the staging path otherwise // continue on failure to delete the stash file, as kubernetes will fail to delete the staging path otherwise