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 {
