Unverified Commit 49109522 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #65529 from…

Merge pull request #65529 from jsafrane/automated-cherry-pick-of-#64882-#65323-upstream-release-1.10 Automatic merge from submit-queue. Automated cherry pick of #64882: Fix UnmountDevice with deleted pod. #65323: Fix cleanup of volume metadata json file. Cherry pick of #64882 #65323 on release-1.10. #64882: Fix UnmountDevice with deleted pod. #65323: Fix cleanup of volume metadata json file. ```release-note Fixed cleanup of CSI metadata files. ```
parents 0016ac3a d0e4db5b
...@@ -245,7 +245,7 @@ func (c *csiAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) { ...@@ -245,7 +245,7 @@ func (c *csiAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) {
return deviceMountPath, nil return deviceMountPath, nil
} }
func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error { func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) (err error) {
glog.V(4).Infof(log("attacher.MountDevice(%s, %s)", devicePath, deviceMountPath)) glog.V(4).Infof(log("attacher.MountDevice(%s, %s)", devicePath, deviceMountPath))
mounted, err := isDirMounted(c.plugin, deviceMountPath) mounted, err := isDirMounted(c.plugin, deviceMountPath)
...@@ -269,6 +269,35 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo ...@@ -269,6 +269,35 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
return err return err
} }
// Store volume metadata for UnmountDevice. Keep it around even if the
// driver does not support NodeStage, UnmountDevice still needs it.
if err = os.MkdirAll(deviceMountPath, 0750); err != nil {
glog.Error(log("attacher.MountDevice failed to create dir %#v: %v", deviceMountPath, err))
return err
}
glog.V(4).Info(log("created target path successfully [%s]", deviceMountPath))
dataDir := filepath.Dir(deviceMountPath)
data := map[string]string{
volDataKey.volHandle: csiSource.VolumeHandle,
volDataKey.driverName: csiSource.Driver,
}
if err = saveVolumeData(dataDir, volDataFileName, data); err != nil {
glog.Error(log("failed to save volume info data: %v", err))
if cleanerr := os.RemoveAll(dataDir); err != nil {
glog.Error(log("failed to remove dir after error [%s]: %v", dataDir, cleanerr))
}
return err
}
defer func() {
if err != nil {
// clean up metadata
glog.Errorf(log("attacher.MountDevice failed: %v", err))
if err := removeMountDir(c.plugin, deviceMountPath); err != nil {
glog.Error(log("attacher.MountDevice failed to remove mount dir after errir [%s]: %v", deviceMountPath, err))
}
}
}()
if c.csiClient == nil { if c.csiClient == nil {
c.csiClient = newCsiDriverClient(csiSource.Driver) c.csiClient = newCsiDriverClient(csiSource.Driver)
} }
...@@ -279,17 +308,18 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo ...@@ -279,17 +308,18 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
// Check whether "STAGE_UNSTAGE_VOLUME" is set // Check whether "STAGE_UNSTAGE_VOLUME" is set
stageUnstageSet, err := hasStageUnstageCapability(ctx, csi) stageUnstageSet, err := hasStageUnstageCapability(ctx, csi)
if err != nil { if err != nil {
glog.Error(log("attacher.MountDevice failed to check STAGE_UNSTAGE_VOLUME: %v", err))
return err return err
} }
if !stageUnstageSet { if !stageUnstageSet {
glog.Infof(log("attacher.MountDevice STAGE_UNSTAGE_VOLUME capability not set. Skipping MountDevice...")) glog.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.
return nil return nil
} }
// Start MountDevice // Start MountDevice
if deviceMountPath == "" { if deviceMountPath == "" {
return fmt.Errorf("attacher.MountDevice failed, deviceMountPath is empty") err = fmt.Errorf("attacher.MountDevice failed, deviceMountPath is empty")
return err
} }
nodeName := string(c.plugin.host.GetNodeName()) nodeName := string(c.plugin.host.GetNodeName())
...@@ -298,13 +328,12 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo ...@@ -298,13 +328,12 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
// search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName // search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
attachment, err := c.k8s.StorageV1beta1().VolumeAttachments().Get(attachID, meta.GetOptions{}) attachment, err := c.k8s.StorageV1beta1().VolumeAttachments().Get(attachID, meta.GetOptions{})
if err != nil { if err != nil {
glog.Error(log("attacher.MountDevice failed while getting volume attachment [id=%v]: %v", attachID, err)) return err // This err already has enough context ("VolumeAttachment xyz not found")
return err
} }
if attachment == nil { if attachment == nil {
glog.Error(log("unable to find VolumeAttachment [id=%s]", attachID)) err = errors.New("no existing VolumeAttachment found")
return errors.New("no existing VolumeAttachment found") return err
} }
publishVolumeInfo := attachment.Status.AttachmentMetadata publishVolumeInfo := attachment.Status.AttachmentMetadata
...@@ -312,18 +341,12 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo ...@@ -312,18 +341,12 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
if csiSource.NodeStageSecretRef != nil { if csiSource.NodeStageSecretRef != nil {
nodeStageSecrets, err = getCredentialsFromSecret(c.k8s, csiSource.NodeStageSecretRef) nodeStageSecrets, err = getCredentialsFromSecret(c.k8s, csiSource.NodeStageSecretRef)
if err != nil { if err != nil {
return fmt.Errorf("fetching NodeStageSecretRef %s/%s failed: %v", err = fmt.Errorf("fetching NodeStageSecretRef %s/%s failed: %v",
csiSource.NodeStageSecretRef.Namespace, csiSource.NodeStageSecretRef.Name, err) csiSource.NodeStageSecretRef.Namespace, csiSource.NodeStageSecretRef.Name, err)
return err
} }
} }
// create target_dir before call to NodeStageVolume
if err := os.MkdirAll(deviceMountPath, 0750); err != nil {
glog.Error(log("attacher.MountDevice failed to create dir %#v: %v", deviceMountPath, err))
return err
}
glog.V(4).Info(log("created target path successfully [%s]", deviceMountPath))
//TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI //TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI
accessMode := v1.ReadWriteOnce accessMode := v1.ReadWriteOnce
if spec.PersistentVolume.Spec.AccessModes != nil { if spec.PersistentVolume.Spec.AccessModes != nil {
...@@ -341,11 +364,6 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo ...@@ -341,11 +364,6 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
csiSource.VolumeAttributes) csiSource.VolumeAttributes)
if err != nil { if err != nil {
glog.Errorf(log("attacher.MountDevice failed: %v", err))
if err := removeMountDir(c.plugin, deviceMountPath); err != nil {
glog.Error(log("attacher.MountDevice failed to remove mount dir after a NodeStageVolume() error [%s]: %v", deviceMountPath, err))
return err
}
return err return err
} }
...@@ -458,10 +476,21 @@ func (c *csiAttacher) UnmountDevice(deviceMountPath string) error { ...@@ -458,10 +476,21 @@ func (c *csiAttacher) UnmountDevice(deviceMountPath string) error {
glog.V(4).Info(log("attacher.UnmountDevice(%s)", deviceMountPath)) glog.V(4).Info(log("attacher.UnmountDevice(%s)", deviceMountPath))
// Setup // Setup
driverName, volID, err := getDriverAndVolNameFromDeviceMountPath(c.k8s, deviceMountPath) var driverName, volID string
if err != nil { dataDir := filepath.Dir(deviceMountPath)
glog.Errorf(log("attacher.UnmountDevice failed to get driver and volume name from device mount path: %v", err)) data, err := loadVolumeData(dataDir, volDataFileName)
return err if err == nil {
driverName = data[volDataKey.driverName]
volID = data[volDataKey.volHandle]
} else {
glog.Error(log("UnmountDevice failed to load volume data file [%s]: %v", dataDir, err))
// The volume might have been mounted by old CSI volume plugin. Fall back to the old behavior: read PV from API server
driverName, volID, err = getDriverAndVolNameFromDeviceMountPath(c.k8s, deviceMountPath)
if err != nil {
glog.Errorf(log("attacher.UnmountDevice failed to get driver and volume name from device mount path: %v", err))
return err
}
} }
if c.csiClient == nil { if c.csiClient == nil {
...@@ -479,6 +508,11 @@ func (c *csiAttacher) UnmountDevice(deviceMountPath string) error { ...@@ -479,6 +508,11 @@ func (c *csiAttacher) UnmountDevice(deviceMountPath string) error {
} }
if !stageUnstageSet { if !stageUnstageSet {
glog.Infof(log("attacher.UnmountDevice STAGE_UNSTAGE_VOLUME capability not set. Skipping UnmountDevice...")) glog.Infof(log("attacher.UnmountDevice STAGE_UNSTAGE_VOLUME capability not set. Skipping UnmountDevice..."))
// Just delete the global directory + json file
if err := removeMountDir(c.plugin, deviceMountPath); err != nil {
return fmt.Errorf("failed to clean up gloubal mount %s: %s", dataDir, err)
}
return nil return nil
} }
...@@ -492,6 +526,11 @@ func (c *csiAttacher) UnmountDevice(deviceMountPath string) error { ...@@ -492,6 +526,11 @@ func (c *csiAttacher) UnmountDevice(deviceMountPath string) error {
return err return err
} }
// Delete the global directory + json file
if err := removeMountDir(c.plugin, deviceMountPath); err != nil {
return fmt.Errorf("failed to clean up gloubal mount %s: %s", dataDir, err)
}
glog.V(4).Infof(log("attacher.UnmountDevice successfully requested NodeStageVolume [%s]", deviceMountPath)) glog.V(4).Infof(log("attacher.UnmountDevice successfully requested NodeStageVolume [%s]", deviceMountPath))
return nil return nil
} }
......
...@@ -18,6 +18,7 @@ package csi ...@@ -18,6 +18,7 @@ package csi
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
...@@ -528,10 +529,6 @@ func TestAttacherMountDevice(t *testing.T) { ...@@ -528,10 +529,6 @@ func TestAttacherMountDevice(t *testing.T) {
deviceMountPath: "path2", deviceMountPath: "path2",
stageUnstageSet: false, stageUnstageSet: false,
}, },
{
testName: "stage_unstage not set no vars should not fail",
stageUnstageSet: false,
},
} }
for _, tc := range testCases { for _, tc := range testCases {
...@@ -610,45 +607,50 @@ func TestAttacherUnmountDevice(t *testing.T) { ...@@ -610,45 +607,50 @@ func TestAttacherUnmountDevice(t *testing.T) {
testName string testName string
volID string volID string
deviceMountPath string deviceMountPath string
jsonFile string
createPV bool
stageUnstageSet bool stageUnstageSet bool
shouldFail bool shouldFail bool
}{ }{
{ {
testName: "normal", testName: "normal, json file exists",
volID: "project/zone/test-vol1", volID: "project/zone/test-vol1",
deviceMountPath: "/tmp/csi-test049507108/plugins/csi/pv/test-pv-name/globalmount", deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
jsonFile: `{"driverName": "csi", "volumeHandle":"project/zone/test-vol1"}`,
createPV: false,
stageUnstageSet: true, stageUnstageSet: true,
}, },
{ {
testName: "no device mount path", testName: "normal, json file doesn't exist -> use PV",
volID: "project/zone/test-vol1", volID: "project/zone/test-vol1",
deviceMountPath: "", deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
stageUnstageSet: true, jsonFile: "",
shouldFail: true, createPV: true,
}, },
{ {
testName: "missing part of device mount path", testName: "invalid json -> use PV",
volID: "project/zone/test-vol1", volID: "project/zone/test-vol1",
deviceMountPath: "/tmp/csi-test049507108/plugins/csi/pv/test-pv-name/globalmount", deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
jsonFile: `{"driverName"}}`,
createPV: true,
stageUnstageSet: true, stageUnstageSet: true,
shouldFail: true,
}, },
{ {
testName: "test volume name mismatch", testName: "no json, no PV.volID",
volID: "project/zone/test-vol1", volID: "",
deviceMountPath: "/tmp/csi-test049507108/plugins/csi/pv/test-pv-name/globalmount", deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
stageUnstageSet: true, jsonFile: "",
createPV: true,
shouldFail: true, shouldFail: true,
}, },
{ {
testName: "stage_unstage not set", testName: "no json, no PV",
volID: "project/zone/test-vol1", volID: "project/zone/test-vol1",
deviceMountPath: "/tmp/csi-test049507108/plugins/csi/pv/test-pv-name/globalmount", deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
stageUnstageSet: false, jsonFile: "",
}, createPV: false,
{ stageUnstageSet: true,
testName: "stage_unstage not set no vars should not fail", shouldFail: true,
stageUnstageSet: false,
}, },
} }
...@@ -665,29 +667,45 @@ func TestAttacherUnmountDevice(t *testing.T) { ...@@ -665,29 +667,45 @@ func TestAttacherUnmountDevice(t *testing.T) {
csiAttacher := attacher.(*csiAttacher) csiAttacher := attacher.(*csiAttacher)
csiAttacher.csiClient = setupClient(t, tc.stageUnstageSet) csiAttacher.csiClient = setupClient(t, tc.stageUnstageSet)
if tc.deviceMountPath != "" {
tc.deviceMountPath = filepath.Join(tmpDir, tc.deviceMountPath)
}
// Add the volume to NodeStagedVolumes // Add the volume to NodeStagedVolumes
cdc := csiAttacher.csiClient.(*fakeCsiDriverClient) cdc := csiAttacher.csiClient.(*fakeCsiDriverClient)
cdc.nodeClient.AddNodeStagedVolume(tc.volID, tc.deviceMountPath) cdc.nodeClient.AddNodeStagedVolume(tc.volID, tc.deviceMountPath)
// Make the PV for this object // Make JSON for this object
if tc.deviceMountPath != "" {
if err := os.MkdirAll(tc.deviceMountPath, 0755); err != nil {
t.Fatalf("error creating directory %s: %s", tc.deviceMountPath, err)
}
}
dir := filepath.Dir(tc.deviceMountPath) dir := filepath.Dir(tc.deviceMountPath)
// dir is now /var/lib/kubelet/plugins/kubernetes.io/csi/pv/{pvname} if tc.jsonFile != "" {
pvName := filepath.Base(dir) dataPath := filepath.Join(dir, volDataFileName)
pv := makeTestPV(pvName, 5, "csi", tc.volID) if err := ioutil.WriteFile(dataPath, []byte(tc.jsonFile), 0644); err != nil {
_, err := csiAttacher.k8s.CoreV1().PersistentVolumes().Create(pv) t.Fatalf("error creating %s: %s", dataPath, err)
if err != nil && !tc.shouldFail { }
t.Fatalf("Failed to create PV: %v", err) }
if tc.createPV {
// Make the PV for this object
pvName := filepath.Base(dir)
pv := makeTestPV(pvName, 5, "csi", tc.volID)
_, err := csiAttacher.k8s.CoreV1().PersistentVolumes().Create(pv)
if err != nil && !tc.shouldFail {
t.Fatalf("Failed to create PV: %v", err)
}
} }
// Run // Run
err = csiAttacher.UnmountDevice(tc.deviceMountPath) err := csiAttacher.UnmountDevice(tc.deviceMountPath)
// Verify // Verify
if err != nil { if err != nil {
if !tc.shouldFail { if !tc.shouldFail {
t.Errorf("test should not fail, but error occurred: %v", err) t.Errorf("test should not fail, but error occurred: %v", err)
} }
return continue
} }
if err == nil && tc.shouldFail { if err == nil && tc.shouldFail {
t.Errorf("test should fail, but no error occurred") t.Errorf("test should fail, but no error occurred")
...@@ -710,6 +728,18 @@ func TestAttacherUnmountDevice(t *testing.T) { ...@@ -710,6 +728,18 @@ func TestAttacherUnmountDevice(t *testing.T) {
t.Errorf("could not find expected staged volume: %s", tc.volID) t.Errorf("could not find expected staged volume: %s", tc.volID)
} }
if tc.jsonFile != "" && !tc.shouldFail {
dataPath := filepath.Join(dir, volDataFileName)
if _, err := os.Stat(dataPath); !os.IsNotExist(err) {
if err != nil {
t.Errorf("error checking file %s: %s", dataPath, err)
} else {
t.Errorf("json file %s should not exists, but it does", dataPath)
}
} else {
t.Logf("json file %s was correctly removed", dataPath)
}
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment