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

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

commit e22d60212d3859b69b64b8b7c4d063856df29665
Author: Chun-Hung Hsiao <[email protected]>
AuthorDate: Thu Apr 4 16:27:48 2019 -0700

    Added capabilities and evolve/devolve helpers for CSI v1.
    
    This patch adds capability helper structures for CSI v1 services, and
    evolve/devolve helpers to convert between CSI v1 protobufs and their
    unversioned counterparts.
    
    Review: https://reviews.apache.org/r/70400
---
 src/CMakeLists.txt            |   1 +
 src/Makefile.am               |   2 +
 src/csi/v1_utils.cpp          | 242 ++++++++++++++++++++++++++++++++++++++++++
 src/csi/v1_utils.hpp          | 218 +++++++++++++++++++++++++++++++++++++
 src/tests/csi_utils_tests.cpp |  50 +++++++--
 5 files changed, 507 insertions(+), 6 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 51c7a04..d36d0be 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -243,6 +243,7 @@ set(CSI_SRC
   csi/v0_volume_manager.cpp
   csi/v1.cpp
   csi/v1_client.cpp
+  csi/v1_utils.cpp
   csi/volume_manager.cpp)
 
 set(DOCKER_SRC
diff --git a/src/Makefile.am b/src/Makefile.am
index 9c0180f..64898df 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1598,6 +1598,8 @@ libcsi_la_SOURCES =                                       
                \
   csi/v1.cpp                                                           \
   csi/v1_client.cpp                                                    \
   csi/v1_client.hpp                                                    \
+  csi/v1_utils.cpp                                                     \
+  csi/v1_utils.hpp                                                     \
   csi/volume_manager.cpp                                               \
   csi/volume_manager.hpp
 
diff --git a/src/csi/v1_utils.cpp b/src/csi/v1_utils.cpp
new file mode 100644
index 0000000..e74138b
--- /dev/null
+++ b/src/csi/v1_utils.cpp
@@ -0,0 +1,242 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "csi/v1_utils.hpp"
+
+using google::protobuf::RepeatedPtrField;
+
+namespace mesos {
+namespace csi {
+namespace v1 {
+
+// Helper for repeated field devolving to `T1` from `T2`.
+template <typename T1, typename T2>
+RepeatedPtrField<T1> devolve(RepeatedPtrField<T2> from)
+{
+  RepeatedPtrField<T1> to;
+  foreach (const T2& value, from) {
+    *to.Add() = devolve(value);
+  }
+
+  return to;
+}
+
+
+types::VolumeCapability::BlockVolume devolve(
+    const VolumeCapability::BlockVolume& block)
+{
+  return types::VolumeCapability::BlockVolume();
+}
+
+
+types::VolumeCapability::MountVolume devolve(
+    const VolumeCapability::MountVolume& mount)
+{
+  types::VolumeCapability::MountVolume result;
+  result.set_fs_type(mount.fs_type());
+  *result.mutable_mount_flags() = mount.mount_flags();
+  return result;
+}
+
+
+types::VolumeCapability::AccessMode devolve(
+    const VolumeCapability::AccessMode& accessMode)
+{
+  types::VolumeCapability::AccessMode result;
+
+  switch (accessMode.mode()) {
+    case VolumeCapability::AccessMode::UNKNOWN: {
+      result.set_mode(types::VolumeCapability::AccessMode::UNKNOWN);
+      break;
+    }
+    case VolumeCapability::AccessMode::SINGLE_NODE_WRITER: {
+      result.set_mode(types::VolumeCapability::AccessMode::SINGLE_NODE_WRITER);
+      break;
+    }
+    case VolumeCapability::AccessMode::SINGLE_NODE_READER_ONLY: {
+      result.set_mode(
+          types::VolumeCapability::AccessMode::SINGLE_NODE_READER_ONLY);
+      break;
+    }
+    case VolumeCapability::AccessMode::MULTI_NODE_READER_ONLY: {
+      result.set_mode(
+          types::VolumeCapability::AccessMode::MULTI_NODE_READER_ONLY);
+      break;
+    }
+    case VolumeCapability::AccessMode::MULTI_NODE_SINGLE_WRITER: {
+      result.set_mode(
+          types::VolumeCapability::AccessMode::MULTI_NODE_SINGLE_WRITER);
+      break;
+    }
+    case VolumeCapability::AccessMode::MULTI_NODE_MULTI_WRITER: {
+      result.set_mode(
+          types::VolumeCapability::AccessMode::MULTI_NODE_MULTI_WRITER);
+      break;
+    }
+    // NOTE: We avoid using a default clause for the following values in
+    // proto3's open enum to enable the compiler to detect missing enum cases
+    // for us. See: https://github.com/google/protobuf/issues/3917
+    case google::protobuf::kint32min:
+    case google::protobuf::kint32max: {
+      UNREACHABLE();
+    }
+  }
+
+  return result;
+}
+
+
+types::VolumeCapability devolve(const VolumeCapability& capability)
+{
+  types::VolumeCapability result;
+
+  switch (capability.access_type_case()) {
+    case VolumeCapability::kBlock: {
+      *result.mutable_block() = devolve(capability.block());
+      break;
+    }
+    case VolumeCapability::kMount: {
+      *result.mutable_mount() = devolve(capability.mount());
+      break;
+    }
+    case VolumeCapability::ACCESS_TYPE_NOT_SET: {
+      break;
+    }
+  }
+
+  if (capability.has_access_mode()) {
+    *result.mutable_access_mode() = devolve(capability.access_mode());
+  }
+
+  return result;
+}
+
+
+RepeatedPtrField<types::VolumeCapability> devolve(
+    const RepeatedPtrField<VolumeCapability>& capabilities)
+{
+  return devolve<types::VolumeCapability>(capabilities);
+}
+
+
+// Helper for repeated field evolving to `T1` from `T2`.
+template <typename T1, typename T2>
+RepeatedPtrField<T1> evolve(RepeatedPtrField<T2> from)
+{
+  RepeatedPtrField<T1> to;
+  foreach (const T2& value, from) {
+    *to.Add() = evolve(value);
+  }
+
+  return to;
+}
+
+
+VolumeCapability::BlockVolume evolve(
+    const types::VolumeCapability::BlockVolume& block)
+{
+  return VolumeCapability::BlockVolume();
+}
+
+
+VolumeCapability::MountVolume evolve(
+    const types::VolumeCapability::MountVolume& mount)
+{
+  VolumeCapability::MountVolume result;
+  result.set_fs_type(mount.fs_type());
+  *result.mutable_mount_flags() = mount.mount_flags();
+  return result;
+}
+
+
+VolumeCapability::AccessMode evolve(
+    const types::VolumeCapability::AccessMode& accessMode)
+{
+  VolumeCapability::AccessMode result;
+
+  switch (accessMode.mode()) {
+    case types::VolumeCapability::AccessMode::UNKNOWN: {
+      result.set_mode(VolumeCapability::AccessMode::UNKNOWN);
+      break;
+    }
+    case types::VolumeCapability::AccessMode::SINGLE_NODE_WRITER: {
+      result.set_mode(VolumeCapability::AccessMode::SINGLE_NODE_WRITER);
+      break;
+    }
+    case types::VolumeCapability::AccessMode::SINGLE_NODE_READER_ONLY: {
+      result.set_mode(VolumeCapability::AccessMode::SINGLE_NODE_READER_ONLY);
+      break;
+    }
+    case types::VolumeCapability::AccessMode::MULTI_NODE_READER_ONLY: {
+      result.set_mode(VolumeCapability::AccessMode::MULTI_NODE_READER_ONLY);
+      break;
+    }
+    case types::VolumeCapability::AccessMode::MULTI_NODE_SINGLE_WRITER: {
+      result.set_mode(VolumeCapability::AccessMode::MULTI_NODE_SINGLE_WRITER);
+      break;
+    }
+    case types::VolumeCapability::AccessMode::MULTI_NODE_MULTI_WRITER: {
+      result.set_mode(VolumeCapability::AccessMode::MULTI_NODE_MULTI_WRITER);
+      break;
+    }
+    // NOTE: We avoid using a default clause for the following values in
+    // proto3's open enum to enable the compiler to detect missing enum cases
+    // for us. See: https://github.com/google/protobuf/issues/3917
+    case google::protobuf::kint32min:
+    case google::protobuf::kint32max: {
+      UNREACHABLE();
+    }
+  }
+
+  return result;
+}
+
+
+VolumeCapability evolve(const types::VolumeCapability& capability)
+{
+  VolumeCapability result;
+
+  switch (capability.access_type_case()) {
+    case types::VolumeCapability::kBlock: {
+      *result.mutable_block() = evolve(capability.block());
+      break;
+    }
+    case types::VolumeCapability::kMount: {
+      *result.mutable_mount() = evolve(capability.mount());
+      break;
+    }
+    case types::VolumeCapability::ACCESS_TYPE_NOT_SET: {
+      break;
+    }
+  }
+
+  if (capability.has_access_mode()) {
+    *result.mutable_access_mode() = evolve(capability.access_mode());
+  }
+
+  return result;
+}
+
+
+RepeatedPtrField<VolumeCapability> devolve(
+    const RepeatedPtrField<types::VolumeCapability>& capabilities)
+{
+  return evolve<VolumeCapability>(capabilities);
+}
+
+} // namespace v1 {
+} // namespace csi {
+} // namespace mesos {
diff --git a/src/csi/v1_utils.hpp b/src/csi/v1_utils.hpp
new file mode 100644
index 0000000..11a64d7
--- /dev/null
+++ b/src/csi/v1_utils.hpp
@@ -0,0 +1,218 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef __CSI_V1_UTILS_HPP__
+#define __CSI_V1_UTILS_HPP__
+
+#include <google/protobuf/message.h>
+
+#include <mesos/csi/types.hpp>
+#include <mesos/csi/v1.hpp>
+
+#include <stout/foreach.hpp>
+#include <stout/unreachable.hpp>
+
+namespace mesos {
+namespace csi {
+namespace v1 {
+
+struct PluginCapabilities
+{
+  PluginCapabilities() = default;
+
+  template <typename Iterable>
+  PluginCapabilities(const Iterable& capabilities)
+  {
+    foreach (const auto& capability, capabilities) {
+      if (capability.has_service() &&
+          PluginCapability::Service::Type_IsValid(
+              capability.service().type())) {
+        switch (capability.service().type()) {
+          case PluginCapability::Service::UNKNOWN:
+            break;
+          case PluginCapability::Service::CONTROLLER_SERVICE:
+            controllerService = true;
+            break;
+          case PluginCapability::Service::VOLUME_ACCESSIBILITY_CONSTRAINTS:
+            volumeAccessibilityConstraints = true;
+            break;
+
+          // NOTE: We avoid using a default clause for the following values in
+          // proto3's open enum to enable the compiler to detect missing enum
+          // cases for us. See: https://github.com/google/protobuf/issues/3917
+          case google::protobuf::kint32min:
+          case google::protobuf::kint32max:
+            UNREACHABLE();
+        }
+      }
+
+      if (capability.has_volume_expansion() &&
+          PluginCapability::VolumeExpansion::Type_IsValid(
+              capability.volume_expansion().type())) {
+        switch (capability.volume_expansion().type()) {
+          case PluginCapability::VolumeExpansion::UNKNOWN:
+            break;
+          case PluginCapability::VolumeExpansion::ONLINE:
+            volumeExpansionOnline = true;
+            volumeExpansionOffline = false;
+            break;
+          case PluginCapability::VolumeExpansion::OFFLINE:
+            volumeExpansionOnline = false;
+            volumeExpansionOffline = true;
+            break;
+
+          // NOTE: We avoid using a default clause for the following values in
+          // proto3's open enum to enable the compiler to detect missing enum
+          // cases for us. See: https://github.com/google/protobuf/issues/3917
+          case google::protobuf::kint32min:
+          case google::protobuf::kint32max:
+            UNREACHABLE();
+        }
+      }
+    }
+  }
+
+  bool controllerService = false;
+  bool volumeAccessibilityConstraints = false;
+  bool volumeExpansionOnline = false;
+  bool volumeExpansionOffline = false;
+};
+
+
+struct ControllerCapabilities
+{
+  ControllerCapabilities() = default;
+
+  template <typename Iterable>
+  ControllerCapabilities(const Iterable& capabilities)
+  {
+    foreach (const auto& capability, capabilities) {
+      if (capability.has_rpc() &&
+          ControllerServiceCapability::RPC::Type_IsValid(
+              capability.rpc().type())) {
+        switch (capability.rpc().type()) {
+          case ControllerServiceCapability::RPC::UNKNOWN:
+            break;
+          case ControllerServiceCapability::RPC::CREATE_DELETE_VOLUME:
+            createDeleteVolume = true;
+            break;
+          case ControllerServiceCapability::RPC::PUBLISH_UNPUBLISH_VOLUME:
+            publishUnpublishVolume = true;
+            break;
+          case ControllerServiceCapability::RPC::LIST_VOLUMES:
+            listVolumes = true;
+            break;
+          case ControllerServiceCapability::RPC::GET_CAPACITY:
+            getCapacity = true;
+            break;
+          case ControllerServiceCapability::RPC::CREATE_DELETE_SNAPSHOT:
+            createDeleteSnapshot = true;
+            break;
+          case ControllerServiceCapability::RPC::LIST_SNAPSHOTS:
+            listSnapshots = true;
+            break;
+          case ControllerServiceCapability::RPC::CLONE_VOLUME:
+            cloneVolume = true;
+            break;
+          case ControllerServiceCapability::RPC::PUBLISH_READONLY:
+            publishReadonly = true;
+            break;
+          case ControllerServiceCapability::RPC::EXPAND_VOLUME:
+            expandVolume = true;
+            break;
+
+          // NOTE: We avoid using a default clause for the following values in
+          // proto3's open enum to enable the compiler to detect missing enum
+          // cases for us. See: https://github.com/google/protobuf/issues/3917
+          case google::protobuf::kint32min:
+          case google::protobuf::kint32max:
+            UNREACHABLE();
+        }
+      }
+    }
+  }
+
+  bool createDeleteVolume = false;
+  bool publishUnpublishVolume = false;
+  bool listVolumes = false;
+  bool getCapacity = false;
+  bool createDeleteSnapshot = false;
+  bool listSnapshots = false;
+  bool cloneVolume = false;
+  bool publishReadonly = false;
+  bool expandVolume = false;
+};
+
+
+struct NodeCapabilities
+{
+  NodeCapabilities() = default;
+
+  template <typename Iterable>
+  NodeCapabilities(const Iterable& capabilities)
+  {
+    foreach (const auto& capability, capabilities) {
+      if (capability.has_rpc() &&
+          NodeServiceCapability::RPC::Type_IsValid(capability.rpc().type())) {
+        switch (capability.rpc().type()) {
+          case NodeServiceCapability::RPC::UNKNOWN:
+            break;
+          case NodeServiceCapability::RPC::STAGE_UNSTAGE_VOLUME:
+            stageUnstageVolume = true;
+            break;
+          case NodeServiceCapability::RPC::GET_VOLUME_STATS:
+            getVolumeStats = true;
+            break;
+          case NodeServiceCapability::RPC::EXPAND_VOLUME:
+            expandVolume = true;
+            break;
+
+          // NOTE: We avoid using a default clause for the following values in
+          // proto3's open enum to enable the compiler to detect missing enum
+          // cases for us. See: https://github.com/google/protobuf/issues/3917
+          case google::protobuf::kint32min:
+          case google::protobuf::kint32max:
+            UNREACHABLE();
+        }
+      }
+    }
+  }
+
+  bool stageUnstageVolume = false;
+  bool getVolumeStats = false;
+  bool expandVolume = false;
+};
+
+
+// Helpers to devolve CSI v1 protobufs to their unversioned counterparts.
+types::VolumeCapability devolve(const VolumeCapability& capability);
+
+google::protobuf::RepeatedPtrField<types::VolumeCapability> devolve(
+    const google::protobuf::RepeatedPtrField<VolumeCapability>& capabilities);
+
+
+// Helpers to evolve unversioned CSI protobufs to their v1 counterparts.
+VolumeCapability evolve(const types::VolumeCapability& capability);
+
+google::protobuf::RepeatedPtrField<VolumeCapability> evolve(
+    const google::protobuf::RepeatedPtrField<types::VolumeCapability>&
+      capabilities);
+
+} // namespace v1 {
+} // namespace csi {
+} // namespace mesos {
+
+#endif // __CSI_V1_UTILS_HPP__
diff --git a/src/tests/csi_utils_tests.cpp b/src/tests/csi_utils_tests.cpp
index b9d0ca8..5ebd0e1 100644
--- a/src/tests/csi_utils_tests.cpp
+++ b/src/tests/csi_utils_tests.cpp
@@ -23,23 +23,62 @@
 
 #include <mesos/csi/types.hpp>
 #include <mesos/csi/v0.hpp>
+#include <mesos/csi/v1.hpp>
 
 #include "csi/v0_utils.hpp"
+#include "csi/v1_utils.hpp"
 
 namespace util = google::protobuf::util;
 
 using std::string;
 using std::vector;
 
+// NOTE: We define the following function templates in proper namespaces so
+// argument-dependent lookup can do the magic to pick up the right functions
+// for a given protobuf in the tests.
+namespace csi {
+namespace v0 {
+
+template <typename T>
+T devolveAndEvolve(const T& t)
+{
+  return mesos::csi::v0::evolve(mesos::csi::v0::devolve(t));
+}
+
+} // namespace v0 {
+
+
+namespace v1 {
+
+template <typename T>
+T devolveAndEvolve(const T& t)
+{
+  return mesos::csi::v1::evolve(mesos::csi::v1::devolve(t));
+}
+
+} // namespace v1 {
+} // namespace csi {
+
+
 namespace mesos {
 namespace internal {
 namespace tests {
 
+template <typename T>
+class CSIUtilsTest : public testing::Test {};
+
+
+using VolumeCapabilityTypes = testing::Types<
+    csi::v0::VolumeCapability,
+    csi::v1::VolumeCapability>;
+
+
+TYPED_TEST_CASE(CSIUtilsTest, VolumeCapabilityTypes);
+
+
 // This test verifies that a versioned CSI `VolumeCapability` protobuf can be
 // devolved to an unversioned protobuf then be evolved back to the same one.
-//
-// TODO(chhsiao): Parameterize this test with CSI versions.
-TEST(CsiUtilsTest, VolumeCapabilityEvolve)
+TYPED_TEST(CSIUtilsTest, DevolveAndEvolve)
 {
   // The following JSON examples contains both valid and invalid CSI volume
   // capabilities. However, no matter if the capability is valid, they should 
be
@@ -155,10 +194,9 @@ TEST(CsiUtilsTest, VolumeCapabilityEvolve)
 
   foreach (const string& example, examples) {
     // NOTE: We use Google's JSON utility functions for proto3.
-    csi::v0::VolumeCapability versioned;
+    TypeParam versioned;
     ASSERT_EQ(util::Status::OK, util::JsonStringToMessage(example, 
&versioned));
-    EXPECT_EQ(versioned, csi::v0::evolve(csi::v0::devolve(versioned)))
-      << example;
+    EXPECT_EQ(versioned, devolveAndEvolve(versioned)) << example;
   }
 }
 

Reply via email to