Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dont remove volumes when saveVolumeData fails #96021

Merged
merged 3 commits into from Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 8 additions & 7 deletions pkg/volume/csi/csi_attacher.go
Expand Up @@ -296,13 +296,8 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
volDataKey.volHandle: csiSource.VolumeHandle,
volDataKey.driverName: csiSource.Driver,
}
if err = saveVolumeData(dataDir, volDataFileName, data); err != nil {
klog.Error(log("failed to save volume info data: %v", err))
if cleanErr := os.RemoveAll(dataDir); cleanErr != nil {
klog.Error(log("failed to remove dir after error [%s]: %v", dataDir, cleanErr))
}
return err
}

err = saveVolumeData(dataDir, volDataFileName, data)
defer func() {
// Only if there was an error and volume operation was considered
// finished, we should remove the directory.
Expand All @@ -315,6 +310,12 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
}
}()

if err != nil {
errMsg := log("failed to save volume info data: %v", err)
klog.Error(errMsg)
return errors.New(errMsg)
}

if !stageUnstageSet {
klog.Infof(log("attacher.MountDevice STAGE_UNSTAGE_VOLUME capability not set. Skipping MountDevice..."))
// defer does *not* remove the metadata file and it's correct - UnmountDevice needs it there.
Expand Down
70 changes: 61 additions & 9 deletions pkg/volume/csi/csi_attacher_test.go
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"reflect"
"sync"
Expand Down Expand Up @@ -1112,15 +1113,16 @@ func TestAttacherMountDevice(t *testing.T) {
transientError := volumetypes.NewTransientOperationFailure("")

testCases := []struct {
testName string
volName string
devicePath string
deviceMountPath string
stageUnstageSet bool
shouldFail bool
createAttachment bool
exitError error
spec *volume.Spec
testName string
volName string
devicePath string
deviceMountPath string
stageUnstageSet bool
shouldFail bool
createAttachment bool
populateDeviceMountPath bool
exitError error
spec *volume.Spec
}{
{
testName: "normal PV",
Expand Down Expand Up @@ -1210,9 +1212,24 @@ func TestAttacherMountDevice(t *testing.T) {
exitError: nonFinalError,
shouldFail: true,
},
{
testName: "failure PV with existing data",
volName: "test-vol1",
devicePath: "path1",
deviceMountPath: "path2",
stageUnstageSet: true,
createAttachment: true,
populateDeviceMountPath: true,
shouldFail: true,
spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), true),
},
}

for _, tc := range testCases {
user, _ := user.Current()
if tc.populateDeviceMountPath && user.Uid == "0" {
t.Skipf("Skipping intentional failure on existing data when running as root.")
}
t.Run(tc.testName, func(t *testing.T) {
t.Logf("Running test case: %s", tc.testName)

Expand Down Expand Up @@ -1254,6 +1271,25 @@ func TestAttacherMountDevice(t *testing.T) {
}()
}

parent := filepath.Dir(tc.deviceMountPath)
filePath := filepath.Join(parent, "newfile")
if tc.populateDeviceMountPath {
// We need to create the deviceMountPath before we Mount,
// so that we can correctly create the file without errors.
err := os.MkdirAll(tc.deviceMountPath, 0750)
if err != nil {
t.Errorf("error attempting to create the directory")
}
_, err = os.Create(filePath)
if err != nil {
t.Errorf("error attempting to populate file on parent path: %v", err)
}
err = os.Chmod(parent, 0555)
if err != nil {
t.Errorf("error attempting to modify directory permissions: %v", err)
}
}

// Run
err := csiAttacher.MountDevice(tc.spec, tc.devicePath, tc.deviceMountPath)

Expand All @@ -1262,6 +1298,22 @@ func TestAttacherMountDevice(t *testing.T) {
if !tc.shouldFail {
t.Errorf("test should not fail, but error occurred: %v", err)
}
if tc.populateDeviceMountPath {
// We're expecting saveVolumeData to fail, which is responsible
// for creating this file. It shouldn't exist.
_, err := os.Stat(parent + "/" + volDataFileName)
if !os.IsNotExist(err) {
t.Errorf("vol_data.json should not exist: %v", err)
}
_, err = os.Stat(filePath)
if os.IsNotExist(err) {
t.Errorf("expecting file to exist after err received: %v", err)
}
err = os.Chmod(parent, 0777)
if err != nil {
t.Errorf("failed to modify permissions after test: %v", err)
}
}
return
}
if err == nil && tc.shouldFail {
Expand Down
39 changes: 31 additions & 8 deletions pkg/volume/csi/csi_plugin.go
Expand Up @@ -43,6 +43,7 @@ import (
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/csi/nodeinfomanager"
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
)

const (
Expand Down Expand Up @@ -456,11 +457,23 @@ func (p *csiPlugin) NewMounter(
attachID := getAttachmentName(volumeHandle, driverName, node)
volData[volDataKey.attachmentID] = attachID

if err := saveVolumeData(dataDir, volDataFileName, volData); err != nil {
if removeErr := os.RemoveAll(dataDir); removeErr != nil {
klog.Error(log("failed to remove dir after error [%s]: %v", dataDir, removeErr))
err = saveVolumeData(dataDir, volDataFileName, volData)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we do same thing in this file for other places where saveVolumeData is being called followed up by os.RemoveAll? This applies to NewBlockVolumeMapper call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this directory same or different than the one used in MountDevice?

Copy link
Contributor Author

@huffmanca huffmanca Jan 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As best I can tell from testing with the hostpath driver, these appear to be different directories:

  • csi_plugin: created path successfully [/var/lib/kubelet/pods/103bb9c7-0a9f-4958-bb6a-8d05f64420f5/volumes/kubernetes.io~csi/pvc-21228cc8-5669-4c0d-bbdc-d7b4e19dfb79]
  • csi_attacher: created target path successfully [/var/lib/kubelet/plugins/kubernetes.io/csi/pv/pvc-22c63cbf-4e2c-4ca6-b269-d5e44a1f3718/globalmount]

defer func() {
// Only if there was an error and volume operation was considered
// finished, we should remove the directory.
if err != nil && volumetypes.IsOperationFinishedError(err) {
// attempt to cleanup volume mount dir.
if err = removeMountDir(p, dir); err != nil {
klog.Error(log("attacher.MountDevice failed to remove mount dir after error [%s]: %v", dir, err))
}
}
return nil, errors.New(log("failed to save volume info data: %v", err))
}()

if err != nil {
errorMsg := log("csi.NewMounter failed to save volume info data: %v", err)
klog.Error(errorMsg)

return nil, errors.New(errorMsg)
}

klog.V(4).Info(log("mounter created successfully"))
Expand Down Expand Up @@ -701,11 +714,21 @@ func (p *csiPlugin) NewBlockVolumeMapper(spec *volume.Spec, podRef *api.Pod, opt
volDataKey.attachmentID: attachID,
}

if err := saveVolumeData(dataDir, volDataFileName, volData); err != nil {
if removeErr := os.RemoveAll(dataDir); removeErr != nil {
klog.Error(log("failed to remove dir after error [%s]: %v", dataDir, removeErr))
err = saveVolumeData(dataDir, volDataFileName, volData)
defer func() {
// Only if there was an error and volume operation was considered
// finished, we should remove the directory.
if err != nil && volumetypes.IsOperationFinishedError(err) {
// attempt to cleanup volume mount dir.
if err = removeMountDir(p, dataDir); err != nil {
klog.Error(log("attacher.MountDevice failed to remove mount dir after error [%s]: %v", dataDir, err))
}
}
return nil, errors.New(log("failed to save volume info data: %v", err))
}()
if err != nil {
errorMsg := log("csi.NewBlockVolumeMapper failed to save volume info data: %v", err)
klog.Error(errorMsg)
return nil, errors.New(errorMsg)
}

return mapper, nil
Expand Down