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 2178019007782ac2bc87a5170a725c4899761db5
Author: Chun-Hung Hsiao <[email protected]>
AuthorDate: Mon Apr 1 23:23:40 2019 -0700

    Added an "unversioned" `VolumeCapability`.
    
    To support both CSI v0 and v1, the "unversioned" `VolumeCapability` is
    designed to satisfy the following compatibility guarantees:
    
    1. The unversioned `VolumeCapability` parsed from a versioned one should
       be able to used to reconstruct the original versioned
       `VolumeCapability`, and can be upgraded/downgraded to a different
       CSI version and preserve as much semantics as possible.
    
    2. If an backward-incompatible change is introduced in future CSI
       `VolumeCapability`, the unversioned `VolumeCapability` can provide a
       way to do a backward compatible upgrade.
    
    Review: https://reviews.apache.org/r/70247/
---
 include/mesos/csi/types.hpp   |  40 +++++++++
 include/mesos/csi/types.proto |  90 ++++++++++++++++++++
 src/CMakeLists.txt            |   2 +
 src/Makefile.am               |  15 ++++
 src/csi/types.cpp             |  36 ++++++++
 src/csi/utils.cpp             | 194 ++++++++++++++++++++++++++++++++++++++++--
 src/csi/utils.hpp             |  10 +++
 src/tests/CMakeLists.txt      |   1 +
 src/tests/csi_utils_tests.cpp | 168 ++++++++++++++++++++++++++++++++++++
 9 files changed, 549 insertions(+), 7 deletions(-)

diff --git a/include/mesos/csi/types.hpp b/include/mesos/csi/types.hpp
new file mode 100644
index 0000000..df9df38
--- /dev/null
+++ b/include/mesos/csi/types.hpp
@@ -0,0 +1,40 @@
+// 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 __MESOS_CSI_TYPES_HPP__
+#define __MESOS_CSI_TYPES_HPP__
+
+// ONLY USEFUL AFTER RUNNING PROTOC.
+#include <mesos/csi/types.pb.h>
+
+namespace mesos {
+namespace csi {
+namespace types {
+
+bool operator==(const VolumeCapability& left, const VolumeCapability& right);
+
+
+inline bool operator!=(
+    const VolumeCapability& left, const VolumeCapability& right)
+{
+  return !(left == right);
+}
+
+} // namespace types {
+} // namespace csi {
+} // namespace mesos {
+
+#endif // __MESOS_CSI_TYPES_HPP__
diff --git a/include/mesos/csi/types.proto b/include/mesos/csi/types.proto
new file mode 100644
index 0000000..3e1ac4b
--- /dev/null
+++ b/include/mesos/csi/types.proto
@@ -0,0 +1,90 @@
+// 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.
+
+syntax = "proto3";
+
+package mesos.csi.types;
+
+// This file contains "unversioned" CSI protobuf definitions used by Mesos. For
+// any CSI version, an unversioned CSI protobuf constructed from a versioned 
one
+// can be used to reconstructed back the same versioned protobuf (excluding
+// unknown fields). An unversioned protobuf can also be used to construct a
+// protobuf of another CSI version, but version-specific details might be lost.
+
+
+/**
+ * Protobuf to specify a capability of a volume:
+ * 
https://github.com/container-storage-interface/spec/blob/release-1.1/spec.md#createvolume
+ */
+message VolumeCapability {
+  // Indicate that the volume will be accessed via the block device API.
+  message BlockVolume {
+    // Intentionally empty, for now.
+  }
+
+  // Indicate that the volume will be accessed via the filesystem API.
+  message MountVolume {
+    // The filesystem type. This field is OPTIONAL.
+    // An empty string is equal to an unspecified field value.
+    string fs_type = 1;
+
+    // The mount options that can be used for the volume. This field is
+    // OPTIONAL. `mount_flags` MAY contain sensitive information.
+    // Therefore, the CO and the Plugin MUST NOT leak this information
+    // to untrusted entities. The total size of this repeated field
+    // SHALL NOT exceed 4 KiB.
+    repeated string mount_flags = 2;
+  }
+
+  // Specify how a volume can be accessed.
+  message AccessMode {
+    enum Mode {
+      UNKNOWN = 0;
+
+      // Can only be published once as read/write on a single node, at
+      // any given time.
+      SINGLE_NODE_WRITER = 1;
+
+      // Can only be published once as readonly on a single node, at
+      // any given time.
+      SINGLE_NODE_READER_ONLY = 2;
+
+      // Can be published as readonly at multiple nodes simultaneously.
+      MULTI_NODE_READER_ONLY = 3;
+
+      // Can be published at multiple nodes simultaneously. Only one of
+      // the node can be used as read/write. The rest will be readonly.
+      MULTI_NODE_SINGLE_WRITER = 4;
+
+      // Can be published as read/write at multiple nodes
+      // simultaneously.
+      MULTI_NODE_MULTI_WRITER = 5;
+    }
+
+    // This field is REQUIRED.
+    Mode mode = 1;
+  }
+
+  // Specifies what API the volume will be accessed using. One of the
+  // following fields MUST be specified.
+  oneof access_type {
+    BlockVolume block = 1;
+    MountVolume mount = 2;
+  }
+
+  // This is a REQUIRED field.
+  AccessMode access_mode = 3;
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f1c3114..2bb2a9f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -41,6 +41,7 @@ PROTOC_GENERATE(TARGET mesos/appc/spec)
 PROTOC_GENERATE(TARGET mesos/authentication/authentication)
 PROTOC_GENERATE(TARGET mesos/authorizer/acls)
 PROTOC_GENERATE(TARGET mesos/authorizer/authorizer)
+PROTOC_GENERATE(TARGET mesos/csi/types)
 PROTOC_GENERATE(TARGET mesos/docker/spec)
 PROTOC_GENERATE(TARGET mesos/docker/v1)
 PROTOC_GENERATE(TARGET mesos/docker/v2)
@@ -241,6 +242,7 @@ set(COMMON_SRC
 
 set(CSI_SRC
   csi/client.cpp
+  csi/types.cpp
   csi/metrics.cpp
   csi/paths.cpp
   csi/rpc.cpp
diff --git a/src/Makefile.am b/src/Makefile.am
index ea8e176..5564b69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -294,6 +294,7 @@ APPC_SPEC_PROTO = 
$(top_srcdir)/include/mesos/appc/spec.proto
 AUTHENTICATION_PROTO = 
$(top_srcdir)/include/mesos/authentication/authentication.proto
 AUTHORIZATION_PROTO = $(top_srcdir)/include/mesos/authorizer/authorizer.proto
 CONTAINERIZER_PROTO = $(top_srcdir)/include/mesos/slave/containerizer.proto
+CSI_TYPES_PROTO = $(top_srcdir)/include/mesos/csi/types.proto
 DOCKER_SPEC_PROTO = $(top_srcdir)/include/mesos/docker/spec.proto
 DOCKER_V1_PROTO = $(top_srcdir)/include/mesos/docker/v1.proto
 DOCKER_V2_PROTO = $(top_srcdir)/include/mesos/docker/v2.proto
@@ -427,6 +428,8 @@ CXX_CSI_PROTOS =                                            
        \
   ../include/csi/csi.grpc.pb.h                                         \
   ../include/csi/csi.pb.cc                                             \
   ../include/csi/csi.pb.h                                              \
+  ../include/mesos/csi/types.pb.cc                                     \
+  ../include/mesos/csi/types.pb.h                                      \
   csi/state.pb.cc                                                      \
   csi/state.pb.h
 
@@ -702,6 +705,15 @@ nodist_authorizer_HEADERS =                                
                \
   ../include/mesos/authorizer/acls.pb.h                                        
\
   ../include/mesos/authorizer/authorizer.pb.h
 
+csidir = $(pkgincludedir)/csi
+
+csi_HEADERS =                                                          \
+  $(top_srcdir)/include/mesos/csi/types.hpp                            \
+  $(top_srcdir)/include/mesos/csi/types.proto
+
+nodist_csi_HEADERS =                                                   \
+  ../include/mesos/csi/types.pb.h
+
 dockerdir = $(pkgincludedir)/docker
 
 docker_HEADERS =                                                       \
@@ -1554,6 +1566,7 @@ noinst_LTLIBRARIES += libcsi.la
 libcsi_la_SOURCES =                                                    \
   csi/client.cpp                                                       \
   csi/client.hpp                                                       \
+  csi/types.cpp                                                                
\
   csi/metrics.cpp                                                      \
   csi/metrics.hpp                                                      \
   csi/paths.cpp                                                                
\
@@ -1643,6 +1656,7 @@ libmesos_la_SOURCES =                                     
                \
   $(AUTHENTICATION_PROTO)                                              \
   $(AUTHORIZATION_PROTO)                                               \
   $(CONTAINERIZER_PROTO)                                               \
+  $(CSI_TYPES_PROTO)                                                   \
   $(EXECUTOR_PROTO)                                                    \
   $(DOCKER_SPEC_PROTO)                                                 \
   $(DOCKER_V1_PROTO)                                                   \
@@ -2521,6 +2535,7 @@ mesos_tests_SOURCES =                                     
        \
   tests/cram_md5_authentication_tests.cpp                      \
   tests/credentials_tests.cpp                                  \
   tests/csi_client_tests.cpp                                   \
+  tests/csi_utils_tests.cpp                                    \
   tests/default_executor_tests.cpp                             \
   tests/disk_profile_adaptor_tests.cpp                         \
   tests/disk_profile_server.hpp                                        \
diff --git a/src/csi/types.cpp b/src/csi/types.cpp
new file mode 100644
index 0000000..cb5ee8f
--- /dev/null
+++ b/src/csi/types.cpp
@@ -0,0 +1,36 @@
+// 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 <mesos/csi/types.hpp>
+
+#include <google/protobuf/util/message_differencer.h>
+
+using google::protobuf::util::MessageDifferencer;
+
+namespace mesos {
+namespace csi {
+namespace types {
+
+bool operator==(const VolumeCapability& left, const VolumeCapability& right)
+{
+  // NOTE: `MessageDifferencer::Equivalent` would ignore unknown fields and 
load
+  // default values for unset fields (which are indistinguishable in proto3).
+  return MessageDifferencer::Equivalent(left, right);
+}
+
+} // namespace types {
+} // namespace csi {
+} // namespace mesos {
diff --git a/src/csi/utils.cpp b/src/csi/utils.cpp
index fd6f95d..872527c 100644
--- a/src/csi/utils.cpp
+++ b/src/csi/utils.cpp
@@ -16,14 +16,9 @@
 
 #include "csi/utils.hpp"
 
-#include <google/protobuf/util/json_util.h>
-
-#include <stout/strings.hpp>
+#include <stout/unreachable.hpp>
 
 using std::ostream;
-using std::string;
-
-using google::protobuf::util::MessageToJsonString;
 
 namespace csi {
 namespace v0 {
@@ -45,7 +40,8 @@ bool operator==(
 }
 
 
-bool operator==(const VolumeCapability& left, const VolumeCapability& right) {
+bool operator==(const VolumeCapability& left, const VolumeCapability& right)
+{
   // NOTE: This enumeration is set when `block` or `mount` are set and
   // covers the case where neither are set.
   if (left.access_type_case() != right.access_type_case()) {
@@ -95,3 +91,187 @@ ostream& operator<<(
 
 } // namespace v0 {
 } // namespace csi {
+
+
+namespace mesos {
+namespace csi {
+namespace v0 {
+
+types::VolumeCapability::BlockVolume devolve(
+    const VolumeCapability::BlockVolume& blockVolume)
+{
+  return types::VolumeCapability::BlockVolume();
+}
+
+
+types::VolumeCapability::MountVolume devolve(
+    const VolumeCapability::MountVolume& mountVolume)
+{
+  types::VolumeCapability::MountVolume result;
+  result.set_fs_type(mountVolume.fs_type());
+  *result.mutable_mount_flags() = mountVolume.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& volumeCapability)
+{
+  types::VolumeCapability result;
+
+  switch (volumeCapability.access_type_case()) {
+    case VolumeCapability::kBlock: {
+      *result.mutable_block() = devolve(volumeCapability.block());
+      break;
+    }
+    case VolumeCapability::kMount: {
+      *result.mutable_mount() = devolve(volumeCapability.mount());
+      break;
+    }
+    case VolumeCapability::ACCESS_TYPE_NOT_SET: {
+      break;
+    }
+  }
+
+  if (volumeCapability.has_access_mode()) {
+    *result.mutable_access_mode() = devolve(volumeCapability.access_mode());
+  }
+
+  return result;
+}
+
+
+VolumeCapability::BlockVolume evolve(
+    const types::VolumeCapability::BlockVolume& blockVolume)
+{
+  return VolumeCapability::BlockVolume();
+}
+
+
+VolumeCapability::MountVolume evolve(
+    const types::VolumeCapability::MountVolume& mountVolume)
+{
+  VolumeCapability::MountVolume result;
+  result.set_fs_type(mountVolume.fs_type());
+  *result.mutable_mount_flags() = mountVolume.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& volumeCapability)
+{
+  VolumeCapability result;
+
+  switch (volumeCapability.access_type_case()) {
+    case types::VolumeCapability::kBlock: {
+      *result.mutable_block() = evolve(volumeCapability.block());
+      break;
+    }
+    case types::VolumeCapability::kMount: {
+      *result.mutable_mount() = evolve(volumeCapability.mount());
+      break;
+    }
+    case types::VolumeCapability::ACCESS_TYPE_NOT_SET: {
+      break;
+    }
+  }
+
+  if (volumeCapability.has_access_mode()) {
+    *result.mutable_access_mode() = evolve(volumeCapability.access_mode());
+  }
+
+  return result;
+}
+
+} // namespace v0 {
+} // namespace csi {
+} // namespace mesos {
diff --git a/src/csi/utils.hpp b/src/csi/utils.hpp
index 9145c67..f1471c7 100644
--- a/src/csi/utils.hpp
+++ b/src/csi/utils.hpp
@@ -28,6 +28,8 @@
 
 #include <mesos/mesos.hpp>
 
+#include <mesos/csi/types.hpp>
+
 #include <stout/foreach.hpp>
 #include <stout/try.hpp>
 #include <stout/unreachable.hpp>
@@ -176,6 +178,14 @@ struct NodeCapabilities
   bool stageUnstageVolume = false;
 };
 
+
+// Helpers to devolve CSI v0 protobufs to their unversioned counterparts.
+types::VolumeCapability devolve(const VolumeCapability& capability);
+
+
+// Helpers to evolve unversioned CSI protobufs to their v0 counterparts.
+VolumeCapability evolve(const types::VolumeCapability& capability);
+
 } // namespace v0 {
 } // namespace csi {
 } // namespace mesos {
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index f3acd82..e6b1d8a 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -91,6 +91,7 @@ set(MESOS_TESTS_SRC
   cram_md5_authentication_tests.cpp
   credentials_tests.cpp
   csi_client_tests.cpp
+  csi_utils_tests.cpp
   default_executor_tests.cpp
   exception_tests.cpp
   executor_http_api_tests.cpp
diff --git a/src/tests/csi_utils_tests.cpp b/src/tests/csi_utils_tests.cpp
new file mode 100644
index 0000000..a0d0c37
--- /dev/null
+++ b/src/tests/csi_utils_tests.cpp
@@ -0,0 +1,168 @@
+// 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 <string>
+#include <vector>
+
+#include <csi/spec.hpp>
+
+#include <google/protobuf/util/json_util.h>
+
+#include <gtest/gtest.h>
+
+#include <mesos/csi/types.hpp>
+
+#include "csi/utils.hpp"
+
+namespace util = google::protobuf::util;
+
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+// 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)
+{
+  // The following JSON examples contains both valid and invalid CSI volume
+  // capabilities. However, no matter if the capability is valid, they should 
be
+  // able to be devolved and evolved back.
+  const vector<string> examples = {
+    // Missing `access_mode`; missing `block` or `mount`.
+    R"~(
+    {}
+    )~",
+
+    // Missing `access_mode.mode`; missing `block` or `mount`.
+    R"~(
+    {
+      "access_mode": {}
+    }
+    )~",
+
+    // Missing `access_mode`.
+    R"~(
+    {
+      "block": {},
+    }
+    )~",
+
+    // Missing `access_mode`.
+    R"~(
+    {
+      "mount": {}
+    }
+    )~",
+
+    // `access_mode.mode` is `UNKNOWN`; missing `block or `mount`.
+    R"~(
+    {
+      "access_mode": {
+        "mode": "UNKNOWN"
+      }
+    }
+    )~",
+
+    // Missing `block` or `mount`.
+    R"~(
+    {
+      "access_mode": {
+        "mode": "SINGLE_NODE_WRITER"
+      }
+    }
+    )~",
+
+    R"~(
+    {
+      "block": {},
+      "access_mode": {
+        "mode": "SINGLE_NODE_WRITER"
+      }
+    }
+    )~",
+
+    R"~(
+    {
+      "mount": {},
+      "access_mode": {
+        "mode": "SINGLE_NODE_WRITER"
+      }
+    }
+    )~",
+
+    R"~(
+    {
+      "mount": {
+        "fs_type": ""
+      },
+      "access_mode": {
+        "mode": "SINGLE_NODE_READER_ONLY"
+      }
+    }
+    )~",
+
+    R"~(
+    {
+      "mount": {
+        "fs_type": "xfs"
+      },
+      "access_mode": {
+        "mode": "MULTI_NODE_READER_ONLY"
+      }
+    }
+    )~",
+
+    R"~(
+    {
+      "mount": {
+        "mount_flags": ["-o", "noatime,nodev,nosuid"]
+      },
+      "access_mode": {
+        "mode": "MULTI_NODE_SINGLE_WRITER"
+      }
+    }
+    )~",
+
+    R"~(
+    {
+      "mount": {
+        "fs_type": "xfs",
+        "mount_flags": ["-o", "noatime,nodev,nosuid"]
+      },
+      "access_mode": {
+        "mode": "MULTI_NODE_MULTI_WRITER"
+      }
+    }
+    )~"
+  };
+
+  foreach (const string& example, examples) {
+    // NOTE: We use Google's JSON utility functions for proto3.
+    csi::v0::VolumeCapability versioned;
+    ASSERT_EQ(util::Status::OK, util::JsonStringToMessage(example, 
&versioned));
+    EXPECT_EQ(versioned, csi::v0::evolve(csi::v0::devolve(versioned)))
+      << example;
+  }
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

Reply via email to