Inferred CSI volume's `readonly` field from volume mode.

Review: https://reviews.apache.org/r/72888
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index a100844..a51d6fa 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -3205,16 +3205,15 @@
       message StaticProvisioning {
         required string volume_id = 1;
         required VolumeCapability volume_capability = 2;
-        optional bool readonly = 3;
 
         // The secrets needed for staging/publishing the volume, e.g.:
         // {
         //   "username": {"type": REFERENCE, "reference": {"name": "U_SECRET"}},
         //   "password": {"type": REFERENCE, "reference": {"name": "P_SECRET"}}
         // }
-        map<string, Secret> node_stage_secrets = 4;
-        map<string, Secret> node_publish_secrets = 5;
-        map<string, string> volume_context = 6;
+        map<string, Secret> node_stage_secrets = 3;
+        map<string, Secret> node_publish_secrets = 4;
+        map<string, string> volume_context = 5;
       }
 
       optional StaticProvisioning static_provisioning = 2;
diff --git a/include/mesos/v1/mesos.proto b/include/mesos/v1/mesos.proto
index 09973ab..ad7092e 100644
--- a/include/mesos/v1/mesos.proto
+++ b/include/mesos/v1/mesos.proto
@@ -3194,16 +3194,15 @@
       message StaticProvisioning {
         required string volume_id = 1;
         required VolumeCapability volume_capability = 2;
-        optional bool readonly = 3;
 
         // The secrets needed for staging/publishing the volume, e.g.:
         // {
         //   "username": {"type": REFERENCE, "reference": {"name": "U_SECRET"}},
         //   "password": {"type": REFERENCE, "reference": {"name": "P_SECRET"}}
         // }
-        map<string, Secret> node_stage_secrets = 4;
-        map<string, Secret> node_publish_secrets = 5;
-        map<string, string> volume_context = 6;
+        map<string, Secret> node_stage_secrets = 3;
+        map<string, Secret> node_publish_secrets = 4;
+        map<string, string> volume_context = 5;
       }
 
       optional StaticProvisioning static_provisioning = 2;
diff --git a/src/slave/containerizer/mesos/isolators/volume/csi/isolator.cpp b/src/slave/containerizer/mesos/isolators/volume/csi/isolator.cpp
index 79a6860..8180b19 100644
--- a/src/slave/containerizer/mesos/isolators/volume/csi/isolator.cpp
+++ b/src/slave/containerizer/mesos/isolators/volume/csi/isolator.cpp
@@ -273,13 +273,6 @@
     const AccessMode& accessMode =
       csiVolume.static_provisioning().volume_capability().access_mode();
 
-    if (csiVolume.static_provisioning().readonly() &&
-        _volume.mode() == Volume::RW) {
-      return Failure(
-          "Cannot publish the volume '" + volumeId +
-          "' in read-only mode but use it in read-write mode");
-    }
-
     if ((accessMode.mode() == AccessMode::SINGLE_NODE_READER_ONLY ||
          accessMode.mode() == AccessMode::MULTI_NODE_READER_ONLY) &&
         _volume.mode() == Volume::RW) {
@@ -355,10 +348,9 @@
     }
 
     Mount mount;
-    mount.csiVolume = csiVolume;
-    mount.volume = volume;
+    mount.volume = _volume;
+    mount.csiVolume = volume;
     mount.target = target;
-    mount.volumeMode = _volume.mode();
 
     mounts.push_back(mount);
     volumeSet.insert(volume);
@@ -390,7 +382,7 @@
   vector<Future<string>> futures;
   futures.reserve(mounts.size());
   foreach (const Mount& mount, mounts) {
-    futures.push_back(csiServer->publishVolume(mount.csiVolume));
+    futures.push_back(csiServer->publishVolume(mount.volume));
   }
 
   return await(futures)
@@ -449,7 +441,7 @@
           continue;
         }
 
-        if (info->volumes.contains(mount.volume)) {
+        if (info->volumes.contains(mount.csiVolume)) {
           isVolumeInUse = true;
           break;
         }
@@ -478,7 +470,7 @@
     *launchInfo.add_mounts() = protobuf::slave::createContainerMount(
         source,
         mount.target,
-        MS_BIND | MS_REC | (mount.volumeMode == Volume::RO ? MS_RDONLY : 0));
+        MS_BIND | MS_REC | (mount.volume.mode() == Volume::RO ? MS_RDONLY : 0));
   }
 
   return launchInfo;
diff --git a/src/slave/containerizer/mesos/isolators/volume/csi/isolator.hpp b/src/slave/containerizer/mesos/isolators/volume/csi/isolator.hpp
index 4349acd..990aa06 100644
--- a/src/slave/containerizer/mesos/isolators/volume/csi/isolator.hpp
+++ b/src/slave/containerizer/mesos/isolators/volume/csi/isolator.hpp
@@ -67,10 +67,9 @@
 private:
   struct Mount
   {
-    Volume::Source::CSIVolume csiVolume;
-    CSIVolume volume;
+    Volume volume;
+    CSIVolume csiVolume;
     std::string target;
-    Volume::Mode volumeMode;
   };
 
   struct Info
diff --git a/src/slave/csi_server.cpp b/src/slave/csi_server.cpp
index 14fa866..3df6200 100644
--- a/src/slave/csi_server.cpp
+++ b/src/slave/csi_server.cpp
@@ -75,8 +75,7 @@
 
 constexpr char DEFAULT_CSI_CONTAINER_PREFIX[] = "mesos-internal-csi-";
 
-static VolumeState createVolumeState(
-    const Volume::Source::CSIVolume::StaticProvisioning& volume);
+static VolumeState createVolumeState(const Volume& volume);
 
 
 static hashset<CSIPluginContainerInfo::Service> extractServices(
@@ -101,7 +100,7 @@
 
   Future<Nothing> start(const SlaveID& _agentId);
 
-  Future<string> publishVolume(const Volume::Source::CSIVolume& volume);
+  Future<string> publishVolume(const Volume& volume);
 
   Future<Nothing> unpublishVolume(
       const string& pluginName,
@@ -363,12 +362,18 @@
 }
 
 
-Future<string> CSIServerProcess::publishVolume(
-    const Volume::Source::CSIVolume& volume)
+Future<string> CSIServerProcess::publishVolume(const Volume& volume)
 {
-  CHECK(volume.has_static_provisioning());
+  CHECK(volume.has_source() &&
+        volume.source().has_type() &&
+        volume.source().type() == Volume::Source::CSI_VOLUME);
 
-  const string& name = volume.plugin_name();
+  CHECK(volume.source().has_csi_volume() &&
+        volume.source().csi_volume().has_static_provisioning());
+
+  const Volume::Source::CSIVolume& csiVolume = volume.source().csi_volume();
+
+  const string& name = csiVolume.plugin_name();
 
   if (!plugins.contains(name)) {
     // This will attempt to load the plugin's configuration, initialize the
@@ -388,13 +393,13 @@
       CHECK(plugins.contains(name));
 
       return plugins.at(name).volumeManager->publishVolume(
-          volume.static_provisioning().volume_id(),
-          createVolumeState(volume.static_provisioning()));
+          csiVolume.static_provisioning().volume_id(),
+          createVolumeState(volume));
       }))
     .then(defer(self(), [=]() {
       CHECK(plugins.contains(name));
 
-      const CSIPluginInfo& info = plugins.at(volume.plugin_name()).info;
+      const CSIPluginInfo& info = plugins.at(csiVolume.plugin_name()).info;
 
       const string mountRootDir = info.has_target_path_root()
           ? info.target_path_root()
@@ -402,7 +407,7 @@
 
       return csi::paths::getMountTargetPath(
           mountRootDir,
-          volume.static_provisioning().volume_id());
+          csiVolume.static_provisioning().volume_id());
     }));
 }
 
@@ -431,14 +436,16 @@
 }
 
 
-VolumeState createVolumeState(
-    const Volume::Source::CSIVolume::StaticProvisioning& volume)
+VolumeState createVolumeState(const Volume& volume)
 {
+  const Volume::Source::CSIVolume::StaticProvisioning& staticProvisioning =
+    volume.source().csi_volume().static_provisioning();
+
   VolumeState result;
   result.set_state(VolumeState::NODE_READY);
-  *result.mutable_volume_capability() = volume.volume_capability();
-  *result.mutable_volume_context() = volume.volume_context();
-  result.set_readonly(volume.readonly());
+  *result.mutable_volume_capability() = staticProvisioning.volume_capability();
+  *result.mutable_volume_context() = staticProvisioning.volume_context();
+  result.set_readonly(volume.mode() == Volume::RO);
   result.set_pre_provisioned(true);
 
   return result;
@@ -533,8 +540,7 @@
 }
 
 
-Future<string> CSIServer::publishVolume(
-    const Volume::Source::CSIVolume& volume)
+Future<string> CSIServer::publishVolume(const Volume& volume)
 {
   return started.future()
     .then(process::defer(
diff --git a/src/slave/csi_server.hpp b/src/slave/csi_server.hpp
index de5c6b6..fb8ef03 100644
--- a/src/slave/csi_server.hpp
+++ b/src/slave/csi_server.hpp
@@ -66,8 +66,7 @@
   // been called, then the publishing of this volume will not be completed until
   // the CSI server is started.
   // Returns the target path at which the volume has been published.
-  process::Future<std::string> publishVolume(
-      const Volume::Source::CSIVolume& volume);
+  process::Future<std::string> publishVolume(const Volume& volume);
 
   // Unpublishes a CSI volume from this agent. If the `start()` method has not
   // yet been called, then the unpublishing of this volume will not be completed
diff --git a/src/tests/containerizer/volume_csi_isolator_tests.cpp b/src/tests/containerizer/volume_csi_isolator_tests.cpp
index d51d3c9..688af4d 100644
--- a/src/tests/containerizer/volume_csi_isolator_tests.cpp
+++ b/src/tests/containerizer/volume_csi_isolator_tests.cpp
@@ -396,9 +396,9 @@
           TEST_CSI_PLUGIN_TYPE,
           TEST_VOLUME_ID,
           TEST_CONTAINER_PATH,
+          mesos::v1::Volume::RW,
           mesos::v1::Volume::Source::CSIVolume::VolumeCapability
-            ::AccessMode::SINGLE_NODE_WRITER,
-          false)}));
+            ::AccessMode::SINGLE_NODE_WRITER)}));
 
   Future<v1::scheduler::Event::Update> startingUpdate;
   Future<v1::scheduler::Event::Update> runningUpdate;
@@ -480,9 +480,9 @@
           TEST_CSI_PLUGIN_TYPE,
           TEST_VOLUME_ID,
           TEST_CONTAINER_PATH,
+          mesos::v1::Volume::RW,
           mesos::v1::Volume::Source::CSIVolume::VolumeCapability
-            ::AccessMode::SINGLE_NODE_WRITER,
-          false)}));
+            ::AccessMode::SINGLE_NODE_WRITER)}));
 
   EXPECT_CALL(
       *scheduler,
@@ -611,9 +611,9 @@
       TEST_CSI_PLUGIN_TYPE,
       TEST_VOLUME_ID,
       TEST_CONTAINER_PATH,
+      mesos::v1::Volume::RW,
       mesos::v1::Volume::Source::CSIVolume::VolumeCapability
-        ::AccessMode::SINGLE_NODE_WRITER,
-      false);
+        ::AccessMode::SINGLE_NODE_WRITER);
 
   taskInfo1.mutable_container()->CopyFrom(
       v1::createContainerInfo("alpine", {volume}));
@@ -804,9 +804,9 @@
           TEST_CSI_PLUGIN_TYPE,
           TEST_VOLUME_ID,
           TEST_CONTAINER_PATH,
+          mesos::v1::Volume::RW,
           mesos::v1::Volume::Source::CSIVolume::VolumeCapability
-            ::AccessMode::SINGLE_NODE_WRITER,
-          false)}));
+            ::AccessMode::SINGLE_NODE_WRITER)}));
 
   Future<v1::scheduler::Event::Update> startingUpdate;
   Future<v1::scheduler::Event::Update> runningUpdate;
@@ -906,9 +906,14 @@
 
   const string pluginName = "org.apache.mesos.csi.added-at-runtime";
 
-  Volume::Source::CSIVolume volume;
-  volume.set_plugin_name(pluginName);
-  volume.mutable_static_provisioning()->CopyFrom(staticVol);
+  Volume::Source::CSIVolume csiVolume;
+  csiVolume.set_plugin_name(pluginName);
+  csiVolume.mutable_static_provisioning()->CopyFrom(staticVol);
+
+  Volume volume;
+  Volume::Source* source = volume.mutable_source();
+  source->set_type(Volume::Source::CSI_VOLUME);
+  source->mutable_csi_volume()->CopyFrom(csiVolume);
 
   // First, perform publish/unpublish calls before we have written the
   // configuration to disk.
@@ -1077,9 +1082,9 @@
           TEST_CSI_PLUGIN_TYPE,
           TEST_VOLUME_ID,
           TEST_CONTAINER_PATH,
+          mesos::v1::Volume::RW,
           mesos::v1::Volume::Source::CSIVolume::VolumeCapability
-            ::AccessMode::SINGLE_NODE_WRITER,
-          false)}));
+            ::AccessMode::SINGLE_NODE_WRITER)}));
 
   Future<v1::scheduler::Event::Update> startingUpdate;
   Future<v1::scheduler::Event::Update> runningUpdate;
@@ -1207,9 +1212,9 @@
           TEST_CSI_PLUGIN_TYPE,
           TEST_VOLUME_ID,
           TEST_CONTAINER_PATH,
+          mesos::v1::Volume::RW,
           mesos::v1::Volume::Source::CSIVolume::VolumeCapability
-            ::AccessMode::SINGLE_NODE_WRITER,
-          false)}));
+            ::AccessMode::SINGLE_NODE_WRITER)}));
 
   Future<v1::scheduler::Event::Update> startingUpdate;
   Future<v1::scheduler::Event::Update> runningUpdate;
@@ -1374,9 +1379,9 @@
           TEST_CSI_PLUGIN_TYPE,
           TEST_VOLUME_ID,
           TEST_CONTAINER_PATH,
+          mesos::v1::Volume::RW,
           mesos::v1::Volume::Source::CSIVolume::VolumeCapability
-            ::AccessMode::SINGLE_NODE_WRITER,
-          false)}));
+            ::AccessMode::SINGLE_NODE_WRITER)}));
 
   v1::ExecutorInfo executorInfo = v1::createExecutorInfo(
       v1::DEFAULT_EXECUTOR_ID,
diff --git a/src/tests/mesos.hpp b/src/tests/mesos.hpp
index 49abfc2..30e8f04 100644
--- a/src/tests/mesos.hpp
+++ b/src/tests/mesos.hpp
@@ -856,14 +856,15 @@
 template <typename TVolume>
 inline TVolume createVolumeCsi(
     const std::string& pluginName,
-    const std::string volumeId,
+    const std::string& volumeId,
     const std::string& containerPath,
+    const typename TVolume::Mode& mode,
     const typename TVolume::Source::CSIVolume::VolumeCapability
-      ::AccessMode::Mode mode,
-    bool readonly)
+      ::AccessMode::Mode& accessMode)
 {
   TVolume volume;
   volume.set_container_path(containerPath);
+  volume.set_mode(mode);
 
   typename TVolume::Source* source = volume.mutable_source();
   source->set_type(TVolume::Source::CSI_VOLUME);
@@ -873,41 +874,9 @@
     source->mutable_csi_volume()->mutable_static_provisioning();
 
   staticInfo->set_volume_id(volumeId);
-  staticInfo->set_readonly(readonly);
   staticInfo->mutable_volume_capability()->mutable_mount();
   staticInfo->mutable_volume_capability()
-    ->mutable_access_mode()->set_mode(mode);
-
-  typedef typename TVolume::Source::CSIVolume::VolumeCapability::AccessMode
-    CSIAccessMode;
-
-  // Set the top-level `mode` field of the volume based on the values of the
-  // CSI access mode and the `readonly` field.
-  typename TVolume::Mode mesosMode;
-
-  switch (mode) {
-    case CSIAccessMode::SINGLE_NODE_WRITER:
-    case CSIAccessMode::MULTI_NODE_SINGLE_WRITER:
-    case CSIAccessMode::MULTI_NODE_MULTI_WRITER: {
-      if (readonly) {
-        mesosMode = TVolume::RO;
-      } else {
-        mesosMode = TVolume::RW;
-      }
-
-      break;
-    }
-
-    case CSIAccessMode::SINGLE_NODE_READER_ONLY:
-    case CSIAccessMode::MULTI_NODE_READER_ONLY:
-    default: {
-      mesosMode = TVolume::RO;
-
-      break;
-    }
-  }
-
-  volume.set_mode(mesosMode);
+    ->mutable_access_mode()->set_mode(accessMode);
 
   return volume;
 }