Added `STAGE_UNSTAGE_VOLUME` capability to the test CSI plugin. Now it is required to call `NodeStageVolume` before `NodePublishVolume` for the test CSI plugin. `NodeStageVolume` would bind-mount the volume directory to the specified staging path, and then `NodePublishVolume` would bind-mount the staging path to the target path.
Review: https://reviews.apache.org/r/66576/ Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/5a105893 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/5a105893 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/5a105893 Branch: refs/heads/master Commit: 5a105893acbae841f68fe95c109a6ff3c3fd63a9 Parents: a9aa3ad Author: Chun-Hung Hsiao <chhs...@apache.org> Authored: Thu Apr 12 19:42:29 2018 -0700 Committer: Chun-Hung Hsiao <chhs...@mesosphere.io> Committed: Thu Apr 12 19:42:29 2018 -0700 ---------------------------------------------------------------------- src/examples/test_csi_plugin.cpp | 144 +++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/5a105893/src/examples/test_csi_plugin.cpp ---------------------------------------------------------------------- diff --git a/src/examples/test_csi_plugin.cpp b/src/examples/test_csi_plugin.cpp index 357e022..9c4da88 100644 --- a/src/examples/test_csi_plugin.cpp +++ b/src/examples/test_csi_plugin.cpp @@ -216,6 +216,16 @@ public: const csi::v0::ControllerGetCapabilitiesRequest* request, csi::v0::ControllerGetCapabilitiesResponse* response) override; + virtual Status NodeStageVolume( + ServerContext* context, + const csi::v0::NodeStageVolumeRequest* request, + csi::v0::NodeStageVolumeResponse* response) override; + + virtual Status NodeUnstageVolume( + ServerContext* context, + const csi::v0::NodeUnstageVolumeRequest* request, + csi::v0::NodeUnstageVolumeResponse* response) override; + virtual Status NodePublishVolume( ServerContext* context, const csi::v0::NodePublishVolumeRequest* request, @@ -594,6 +604,112 @@ Status TestCSIPlugin::ControllerGetCapabilities( } +Status TestCSIPlugin::NodeStageVolume( + ServerContext* context, + const csi::v0::NodeStageVolumeRequest* request, + csi::v0::NodeStageVolumeResponse* response) +{ + LOG(INFO) << request->GetDescriptor()->name() << " '" << *request << "'"; + + // TODO(chhsiao): Validate required fields. + + if (!volumes.contains(request->volume_id())) { + return Status( + grpc::NOT_FOUND, + "Volume '" + request->volume_id() + "' is not found"); + } + + const VolumeInfo& volumeInfo = volumes.at(request->volume_id()); + const string path = getVolumePath(volumeInfo); + + auto it = request->volume_attributes().find("path"); + if (it == request->volume_attributes().end() || it->second != path) { + return Status(grpc::INVALID_ARGUMENT, "Invalid volume attributes"); + } + + if (!os::exists(request->staging_target_path())) { + return Status( + grpc::INVALID_ARGUMENT, + "Target path '" + request->staging_target_path() + "' is not found"); + } + + Try<fs::MountInfoTable> table = fs::MountInfoTable::read(); + if (table.isError()) { + return Status( + grpc::INTERNAL, + "Failed to get mount table: " + table.error()); + } + + foreach (const fs::MountInfoTable::Entry& entry, table->entries) { + if (entry.target == request->staging_target_path()) { + return Status::OK; + } + } + + Try<Nothing> mount = fs::mount( + path, + request->staging_target_path(), + None(), + MS_BIND, + None()); + + if (mount.isError()) { + return Status( + grpc::INTERNAL, + "Failed to mount from '" + path + "' to '" + + request->staging_target_path() + "': " + mount.error()); + } + + return Status::OK; +} + + +Status TestCSIPlugin::NodeUnstageVolume( + ServerContext* context, + const csi::v0::NodeUnstageVolumeRequest* request, + csi::v0::NodeUnstageVolumeResponse* response) +{ + LOG(INFO) << request->GetDescriptor()->name() << " '" << *request << "'"; + + // TODO(chhsiao): Validate required fields. + + if (!volumes.contains(request->volume_id())) { + return Status( + grpc::NOT_FOUND, + "Volume '" + request->volume_id() + "' is not found"); + } + + Try<fs::MountInfoTable> table = fs::MountInfoTable::read(); + if (table.isError()) { + return Status( + grpc::INTERNAL, + "Failed to get mount table: " + table.error()); + } + + bool found = false; + foreach (const fs::MountInfoTable::Entry& entry, table->entries) { + if (entry.target == request->staging_target_path()) { + found = true; + break; + } + } + + if (!found) { + return Status::OK; + } + + Try<Nothing> unmount = fs::unmount(request->staging_target_path()); + if (unmount.isError()) { + return Status( + grpc::INTERNAL, + "Failed to unmount '" + request->staging_target_path() + + "': " + unmount.error()); + } + + return Status::OK; +} + + Status TestCSIPlugin::NodePublishVolume( ServerContext* context, const csi::v0::NodePublishVolumeRequest* request, @@ -623,6 +739,12 @@ Status TestCSIPlugin::NodePublishVolume( "Target path '" + request->target_path() + "' is not found"); } + if (request->staging_target_path().empty()) { + return Status( + grpc::FAILED_PRECONDITION, + "Expecting 'staging_target_path' to be set"); + } + Try<fs::MountInfoTable> table = fs::MountInfoTable::read(); if (table.isError()) { return Status( @@ -630,6 +752,20 @@ Status TestCSIPlugin::NodePublishVolume( "Failed to get mount table: " + table.error()); } + bool found = false; + foreach (const fs::MountInfoTable::Entry& entry, table->entries) { + if (entry.target == request->staging_target_path()) { + found = true; + break; + } + } + + if (!found) { + return Status( + grpc::FAILED_PRECONDITION, + "Volume '" + request->volume_id() + "' has not been staged yet"); + } + foreach (const fs::MountInfoTable::Entry& entry, table->entries) { if (entry.target == request->target_path()) { return Status::OK; @@ -637,7 +773,7 @@ Status TestCSIPlugin::NodePublishVolume( } Try<Nothing> mount = fs::mount( - path, + request->staging_target_path(), request->target_path(), None(), MS_BIND, @@ -700,9 +836,6 @@ Status TestCSIPlugin::NodeUnpublishVolume( return Status::OK; } - const VolumeInfo& volumeInfo = volumes.at(request->volume_id()); - const string path = getVolumePath(volumeInfo); - Try<Nothing> unmount = fs::unmount(request->target_path()); if (unmount.isError()) { return Status( @@ -735,6 +868,9 @@ Status TestCSIPlugin::NodeGetCapabilities( { LOG(INFO) << request->GetDescriptor()->name() << " '" << *request << "'"; + response->add_capabilities()->mutable_rpc()->set_type( + csi::v0::NodeServiceCapability::RPC::STAGE_UNSTAGE_VOLUME); + return Status::OK; }