-
Notifications
You must be signed in to change notification settings - Fork 40.1k
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
CSI MountDevice/UnmountDevice Implementation #60115
Conversation
/assign saad-ali |
10dfa38
to
1eb33d9
Compare
/assign vladimirvivien |
2f64af5
to
f8d0d31
Compare
pkg/volume/csi/csi_attacher.go
Outdated
return "", fmt.Errorf("attacher.GetDeviceMountPath failed, driver name empty") | ||
} | ||
|
||
deviceMountPath := path.Join(c.plugin.host.GetPluginDir(csiSource.Driver), "mounts", csiSource.VolumeHandle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be {pluginDir}/csi/{csiDriverName}/mounts/{csiVolumeHandle}
? Otherwise if someone names their plugin kubernetes.io/gce-pd
they could stomp on the internal GCE PD directories right?
Also instead of "mounts"
use mount.MountsInGlobalPDPath
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And maybe sanitize the volume and driver names (EscapeQualifiedNameForDisk
or something like that)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davidz627 as a followup to Saad's, I would use the method defined here https://github.com/davidz627/kubernetes/blob/f8d0d3162ada15d8a28aed55431d3e66b7fec30f/pkg/volume/csi/csi_mounter.go#L441 as a central location to generate the device mount path. Make all adjustment there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
pkg/kubelet/kubelet_getters.go
Outdated
// getPluginDir returns the pluginName for the given pluginDir that | ||
// was created by getPluginDir | ||
func (kl *Kubelet) getPluginNameFromDir(pluginDir string) string { | ||
return filepath.Base(pluginDir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've defined plugin path as:
deviceMountPath := path.Join(c.plugin.host.GetPluginDir(csiSource.Driver), "mounts", csiSource.VolumeHandle)
So wouldn't this just return the csiSource.VolumeHandle
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh it looks like getDriverAndVolNameFromDeviceMountPath(...)
is called first that splits at /
boundaries, and then getPluginNameFromDir(...)
is called to finish everything off.
Why not just do GetPluginDir(...)
inside getPluginNameFromDir(...)
and move this logic there? That way you could eliminate this method all together. It doesn't look like it is used anywhere else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overhauled to use pv.
pkg/volume/csi/csi_attacher.go
Outdated
glog.V(4).Infof(log("attacher.MountDevice(%s, %s)", devicePath, deviceMountPath)) | ||
|
||
// Setup | ||
ctx, cancel := grpctx.WithTimeout(grpctx.Background(), csiTimeout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move creating ctx
down to right before you actually use it for the first time. No need to waste cycles if you end up not using it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
pkg/volume/csi/csi_attacher.go
Outdated
return fmt.Errorf("attacher.MountDevice failed while getting node capabilities: %v", err) | ||
} | ||
|
||
if capabilities == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this nil check inside hasStageUnstageCapability(...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
pkg/volume/csi/csi_attacher.go
Outdated
return fmt.Errorf("attacher.MountDevice failed, deviceMountPath is empty") | ||
} | ||
|
||
mounted, err := isDirMounted(c.plugin, deviceMountPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you put this check first, and if it is false, then create a context, client, etc.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
pkg/volume/csi/csi_attacher.go
Outdated
} | ||
|
||
nodeName := string(c.plugin.host.GetNodeName()) | ||
fmt.Printf("voldid: %v, drivername: %v, nodeName: %v", csiSource.VolumeHandle, csiSource.Driver, nodeName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember to remove debug statement or convert to glog with appropriate verbosity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
pkg/volume/csi/csi_attacher.go
Outdated
|
||
// probe driver | ||
// TODO (vladimirvivien) move probe call where it is done only when it is needed. | ||
if err := csi.NodeProbe(ctx, csiVersion); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove probe call altogether. It has been removed in the latest (HEAD) version of CSI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
pkg/volume/csi/csi_attacher.go
Outdated
publishVolumeInfo := attachment.Status.AttachmentMetadata | ||
|
||
// get volume attributes | ||
// TODO: for alpha vol attributes are passed via PV.Annotations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure to do a follow up PR to fix this, create a bug or something to remind you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR #58762 has the fix. Simply spec.VolumeAttribs
as the parameter in call to NodeStageVolume
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was in source.VolumeAttributes. lmk if this is not the correct one you were referring to. Looks right to me.
pkg/volume/csi/csi_attacher.go
Outdated
return stageUnstageSet, nil | ||
} | ||
|
||
func getDriverAndVolNameFromDeviceMountPath(deviceMountPath string, plugin *csiPlugin) (string, string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh man this is a fun bit of code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be helpful to document the format deviceMountPath (i.e. {pluginDir}/csi/etc
so future maintainers would have an idea why this works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please take a look at comments.
pkg/volume/csi/csi_attacher.go
Outdated
return "", fmt.Errorf("attacher.GetDeviceMountPath failed, driver name empty") | ||
} | ||
|
||
deviceMountPath := path.Join(c.plugin.host.GetPluginDir(csiSource.Driver), "mounts", csiSource.VolumeHandle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davidz627 as a followup to Saad's, I would use the method defined here https://github.com/davidz627/kubernetes/blob/f8d0d3162ada15d8a28aed55431d3e66b7fec30f/pkg/volume/csi/csi_mounter.go#L441 as a central location to generate the device mount path. Make all adjustment there.
pkg/volume/csi/csi_attacher.go
Outdated
attachID := getAttachmentName(csiSource.VolumeHandle, csiSource.Driver, nodeName) | ||
|
||
// ensure version is supported | ||
if err := csi.AssertSupportedVersion(ctx, csiVersion); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We dont have to asssertSupportedVersion anymore that if block can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed for MountDevice. Still exists in SetUpAt and TearDownAt unless thats been changed in the last day or two.
pkg/volume/csi/csi_attacher.go
Outdated
publishVolumeInfo := attachment.Status.AttachmentMetadata | ||
|
||
// get volume attributes | ||
// TODO: for alpha vol attributes are passed via PV.Annotations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR #58762 has the fix. Simply spec.VolumeAttribs
as the parameter in call to NodeStageVolume
pkg/volume/csi/csi_attacher.go
Outdated
return stageUnstageSet, nil | ||
} | ||
|
||
func getDriverAndVolNameFromDeviceMountPath(deviceMountPath string, plugin *csiPlugin) (string, string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be helpful to document the format deviceMountPath (i.e. {pluginDir}/csi/etc
so future maintainers would have an idea why this works.
pkg/volume/csi/csi_attacher.go
Outdated
} | ||
volID := strings.Join(splitted[len(splitted)-3:], "/") | ||
dir := strings.Join(splitted[:len(splitted)-4], "/") | ||
pluginName := plugin.host.GetPluginNameFromDir(dir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming this{pluginDir}/csi/{csiDriverName}/mounts/{csiVolumeHandle}
is the format, then
strings.Join(splitted[len(splitted)-3:], "/")
will return slice like {csiDriverName}/mounts/{csiVolumeHandle}
. Uneasy about the hardcoded offsets, seems a bit brittle.
Also, consider using filepath.Base(), filepath.Dir(), filepath.Join() for more portable path nomenclatures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the hardcoded offesets are brittle but I couldn't find a better way to do this without a hardcoded number of "Dir"'s anyway (3 of them). Because {csiVolumeHandle} itself is a 3 part object of form project/zone/name
. That fact isn't really captured anywhere (maybe I should have a comment). Open to suggestions for better ways to do this.
I tried using Rel
which ended up being more brittle because even small changes or differences to directory structure would completely destroy the resulting path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overhauled. PTAL and review new versions of makeDeviceMountPath and getDriverAndVolNameFromDeviceMountPath.
f8d0d31
to
870c949
Compare
@saad-ali @vladimirvivien Addressed all comments. Biggest change is in |
@vladimirvivien the PV method in Since there are no character restrictions in the CSI spec on volume handles there were no guaranteed ways to parse the volume handle out of the DeviceMountPath even with sanitation. |
870c949
to
1773385
Compare
/retest |
1773385
to
eeae057
Compare
/retest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost there!
} | ||
|
||
func getDriverAndVolNameFromDeviceMountPath(k8s kubernetes.Interface, deviceMountPath string) (string, string, error) { | ||
// deviceMountPath structure: /var/lib/kubelet/plugins/kubernetes.io/csi/pv/{pvname}/globalmount |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe sanity check assert that path terminates in /globalmount
or /globalmount/
?
return err | ||
} | ||
|
||
req := &csipb.NodeStageVolumeRequest{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pkg/volume/csi/csi_attacher.go
Outdated
return fmt.Errorf("attacher.MountDevice failed while getting node capabilities: %v", err) | ||
} | ||
|
||
stageUnstageSet, err := hasStageUnstageCapability(capabilities) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you always call NodeGetCapabilities(...)
before hasStageUnstageCapability(...)
. Maybe move NodeGetCapabilities(...)
inside hasStageUnstageCapability(...)
@davidz627 taking a look right now. |
eeae057
to
1e6add3
Compare
/retest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
/approve
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some comments. Getting there.
if file := filepath.Base(deviceMountPath); file != globalMountInGlobalPath { | ||
return "", "", fmt.Errorf("getDriverAndVolNameFromDeviceMountPath failed, path did not end in %s", globalMountInGlobalPath) | ||
} | ||
// dir is now /var/lib/kubelet/plugins/kubernetes.io/csi/pv/{pvname} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is {pvname}
sanitized elsewhere (to remove path demarkation chars) ? If so, it probably need to return back to its original state. If no sanitization occurred, ignore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not doing sanitation anywhere, so the name should be same in both the creation and the extraction.
pvName := filepath.Base(dir) | ||
|
||
// Get PV and check for errors | ||
pv, err := k8s.CoreV1().PersistentVolumes().Get(pvName, meta.GetOptions{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to worry about namespace
here? If not, there maybe name collision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PV's are not namespaced in Kubernetes.
} | ||
|
||
// Get VolumeHandle and PluginName from pv | ||
csiSource := pv.Spec.CSI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do a nil check here for pv.Spec.CSI
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 5 lines above already
/retest |
Will let @vladimirvivien review and LGTM /lgtm cancel |
/approve |
/retest |
…nstageVolume for CSI Volume Plugin. Added related unit tests. Vendored CSI Spec to HEAD
1e6add3
to
cbd1896
Compare
/retest |
/retest |
/LGTM |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: davidz627, saad-ali, vladimirvivien The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/test all [submit-queue is verifying that this PR is safe to merge] |
Automatic merge from submit-queue (batch tested with PRs 60430, 60115, 58052, 60355, 60116). If you want to cherry-pick this change to another branch, please follow the instructions here. |
Fixes #60114
What this PR does / why we need it:
This PR Implements MountDevice and UnmountDevice for the CSI Plugin, the functions will call through to NodeStageVolume/NodeUnstageVolume for CSI plugins.
/sig storage