This is an automated email from the ASF dual-hosted git repository.

chhsiao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 75d718f3141d073593c4608bbe8d8027e1e1123a
Author: Chun-Hung Hsiao <[email protected]>
AuthorDate: Wed Feb 6 12:29:37 2019 -0800

    Added a SLRP unit test for persistent block volume creation.
    
    Test `CreateDestroyPersistentBlockVolume` verifies that SLRP would fail
    a `CREATE` operation on a BLOCK disk resource, and a followup `DESTROY`
    will be dropped (instead of failing the SLRP).
    
    Review: https://reviews.apache.org/r/69954
---
 src/tests/mock_csi_plugin.cpp                      |  30 +++-
 .../storage_local_resource_provider_tests.cpp      | 162 +++++++++++++++++++++
 2 files changed, 190 insertions(+), 2 deletions(-)

diff --git a/src/tests/mock_csi_plugin.cpp b/src/tests/mock_csi_plugin.cpp
index dacdc15..82dae64 100644
--- a/src/tests/mock_csi_plugin.cpp
+++ b/src/tests/mock_csi_plugin.cpp
@@ -16,6 +16,8 @@
 
 #include "tests/mock_csi_plugin.hpp"
 
+#include <algorithm>
+
 #include <stout/bytes.hpp>
 
 using std::string;
@@ -61,8 +63,20 @@ MockCSIPlugin::MockCSIPlugin()
   EXPECT_CALL(*this, Probe(_, _, A<csi::v0::ProbeResponse*>()))
     .WillRepeatedly(Return(Status::OK));
 
+  // Return a success by default for testing with the test CSI plugin in
+  // forwarding mode.
   EXPECT_CALL(*this, CreateVolume(_, _, A<csi::v0::CreateVolumeResponse*>()))
-    .WillRepeatedly(Return(Status::OK));
+    .WillRepeatedly(Invoke([](
+        ServerContext* context,
+        const csi::v0::CreateVolumeRequest* request,
+        csi::v0::CreateVolumeResponse* response) {
+      response->mutable_volume()->set_capacity_bytes(std::max(
+          request->capacity_range().required_bytes(),
+          request->capacity_range().limit_bytes()));
+      response->mutable_volume()->set_id(request->name());
+
+      return Status::OK;
+    }));
 
   EXPECT_CALL(*this, DeleteVolume(_, _, A<csi::v0::DeleteVolumeResponse*>()))
     .WillRepeatedly(Return(Status::OK));
@@ -169,8 +183,20 @@ MockCSIPlugin::MockCSIPlugin()
   EXPECT_CALL(*this, Probe(_, _, A<csi::v1::ProbeResponse*>()))
     .WillRepeatedly(Return(Status::OK));
 
+  // Return a success by default for testing with the test CSI plugin in
+  // forwarding mode.
   EXPECT_CALL(*this, CreateVolume(_, _, A<csi::v1::CreateVolumeResponse*>()))
-    .WillRepeatedly(Return(Status::OK));
+    .WillRepeatedly(Invoke([](
+        ServerContext* context,
+        const csi::v1::CreateVolumeRequest* request,
+        csi::v1::CreateVolumeResponse* response) {
+      response->mutable_volume()->set_capacity_bytes(std::max(
+          request->capacity_range().required_bytes(),
+          request->capacity_range().limit_bytes()));
+      response->mutable_volume()->set_volume_id(request->name());
+
+      return Status::OK;
+    }));
 
   EXPECT_CALL(*this, DeleteVolume(_, _, A<csi::v1::DeleteVolumeResponse*>()))
     .WillRepeatedly(Return(Status::OK));
diff --git a/src/tests/storage_local_resource_provider_tests.cpp 
b/src/tests/storage_local_resource_provider_tests.cpp
index 09e7ca0..efc03c2 100644
--- a/src/tests/storage_local_resource_provider_tests.cpp
+++ b/src/tests/storage_local_resource_provider_tests.cpp
@@ -501,6 +501,23 @@ static bool isMountDisk(const Resource& r, const string& 
profile)
 }
 
 
+// Tests whether a resource is a BLOCK disk of a given profile but not a
+// persistent volume. A BLOCK disk has both profile and source ID set.
+template <typename Resource>
+static bool isBlockDisk(const Resource& r, const string& profile)
+{
+  return r.has_disk() &&
+    r.disk().has_source() &&
+    r.disk().source().type() == Resource::DiskInfo::Source::BLOCK &&
+    r.disk().source().has_vendor() &&
+    r.disk().source().vendor() == TEST_CSI_VENDOR &&
+    r.disk().source().has_id() &&
+    r.disk().source().has_profile() &&
+    r.disk().source().profile() == profile &&
+    !r.disk().has_persistence();
+}
+
+
 // Tests whether a resource is a preprovisioned volume. A preprovisioned volume
 // is a RAW disk resource with a source ID but no profile.
 template <typename Resource>
@@ -2968,6 +2985,151 @@ TEST_P(
 }
 
 
+// This test verifies that the storage local resource provider would fail to
+// create a persistent volume on a BLOCK disk resource.
+//
+// TODO(chhsiao): Update this test once persistent BLOCK volumes are supported.
+TEST_P(StorageLocalResourceProviderTest, CreatePersistentBlockVolume)
+{
+  const string profilesPath = path::join(sandbox.get(), "profiles.json");
+  Try<string> blockDiskProfileMapping = strings::format(
+      R"~(
+      {
+        "profile_matrix": {
+          "test": {
+            "csi_plugin_type_selector": {
+              "plugin_type": "%s"
+            },
+            "volume_capabilities": {
+              "block": {},
+              "access_mode": {
+                "mode": "SINGLE_NODE_WRITER"
+              }
+            }
+          }
+        }
+      }
+      )~",
+      TEST_CSI_PLUGIN_TYPE);
+
+  ASSERT_SOME(blockDiskProfileMapping);
+  ASSERT_SOME(os::write(profilesPath, blockDiskProfileMapping.get()));
+
+  loadUriDiskProfileAdaptorModule(profilesPath);
+
+  const string mockCsiEndpoint =
+    "unix://" + path::join(sandbox.get(), "mock_csi.sock");
+
+  // We use a mock CSI plugin here because the test CSI plugin does not support
+  // block volumes yet.
+  MockCSIPlugin plugin;
+  ASSERT_SOME(plugin.startup(mockCsiEndpoint));
+
+  setupResourceProviderConfig(Bytes(0), None(), None(), mockCsiEndpoint);
+
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  Owned<MasterDetector> detector = master.get()->createDetector();
+
+  slave::Flags slaveFlags = CreateSlaveFlags();
+  slaveFlags.disk_profile_adaptor = URI_DISK_PROFILE_ADAPTOR_NAME;
+
+  Future<SlaveRegisteredMessage> slaveRegisteredMessage =
+    FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+  Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), slaveFlags);
+  ASSERT_SOME(slave);
+
+  AWAIT_READY(slaveRegisteredMessage);
+
+  // Register a framework to exercise operations.
+  FrameworkInfo framework = DEFAULT_FRAMEWORK_INFO;
+  framework.set_roles(0, "storage");
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, framework, master.get()->pid, DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(sched, registered(&driver, _, _));
+
+  // We use the following filter to filter offers that do not have wanted
+  // resources for 365 days (the maximum).
+  Filters declineFilters;
+  declineFilters.set_refuse_seconds(Days(365).secs());
+
+  // Decline unwanted offers. The master can send such offers before the
+  // resource provider receives profile updates.
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillRepeatedly(DeclineOffers(declineFilters));
+
+  Future<vector<Offer>> offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, OffersHaveAnyResource(
+      std::bind(isStoragePool<Resource>, lambda::_1, "test"))))
+    .WillOnce(FutureArg<1>(&offers));
+
+  driver.start();
+
+  AWAIT_READY(offers);
+  ASSERT_EQ(1u, offers->size());
+
+  Offer offer = offers->at(0);
+
+  // Create a BLOCK disk.
+  Resource raw = *Resources(offer.resources())
+    .filter(std::bind(isStoragePool<Resource>, lambda::_1, "test"))
+    .begin();
+
+  EXPECT_CALL(sched, resourceOffers(&driver, OffersHaveAnyResource(
+      std::bind(isBlockDisk<Resource>, lambda::_1, "test"))))
+    .WillOnce(FutureArg<1>(&offers));
+
+  // We use the following filter so that the resources will not be filtered for
+  // 5 seconds (the default).
+  Filters acceptFilters;
+  acceptFilters.set_refuse_seconds(0);
+
+  driver.acceptOffers(
+      {offer.id()},
+      {CREATE_DISK(raw, Resource::DiskInfo::Source::BLOCK)},
+      acceptFilters);
+
+  AWAIT_READY(offers);
+  ASSERT_EQ(1u, offers->size());
+
+  offer = offers->at(0);
+
+  Resource created = *Resources(offer.resources())
+    .filter(std::bind(isBlockDisk<Resource>, lambda::_1, "test"))
+    .begin();
+
+  // Create a persistent BLOCK volume, which would fail.
+  Resource persistentVolume = created;
+  persistentVolume.mutable_disk()->mutable_persistence()
+    ->set_id(id::UUID::random().toString());
+  persistentVolume.mutable_disk()->mutable_persistence()
+    ->set_principal(framework.principal());
+  persistentVolume.mutable_disk()->mutable_volume()
+    ->set_container_path("volume");
+  persistentVolume.mutable_disk()->mutable_volume()->set_mode(Volume::RW);
+
+  Future<UpdateOperationStatusMessage> createOperationStatus =
+    FUTURE_PROTOBUF(UpdateOperationStatusMessage(), _, _);
+
+  EXPECT_CALL(
+      sched, resourceOffers(&driver, OffersHaveResource(created)))
+    .WillOnce(FutureArg<1>(&offers));
+
+  driver.acceptOffers({offer.id()}, {CREATE(persistentVolume)}, acceptFilters);
+
+  AWAIT_READY(createOperationStatus);
+  EXPECT_EQ(OPERATION_FAILED, createOperationStatus->status().state());
+
+  AWAIT_EXPECT_READY(offers)
+    << "Failed to wait for an offer containing resource '" << created << "'";
+}
+
+
 // This test verifies that if the storage local resource provider fails to 
clean
 // up a persistent volume, the volume will not be destroyed.
 //

Reply via email to