From 87044036a42b889efe5e73a0edd7c44dfb9182b3 Mon Sep 17 00:00:00 2001 From: Yan Xue <3491507+yxue@users.noreply.github.com> Date: Wed, 18 Mar 2020 08:18:01 -0700 Subject: [PATCH] utility: convert typed_config to factory config (#10418) Convert typed_config to factory config Risk Level: Low Testing: Unit test Signed-off-by: Yan Xue --- source/common/config/utility.h | 25 ++++++++++++++++++++- test/common/config/utility_test.cc | 36 ++++++++++++++++++++++++++++++ test/mocks/config/mocks.cc | 1 + test/mocks/config/mocks.h | 11 +++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/source/common/config/utility.h b/source/common/config/utility.h index 108e14e587c4..ce7b0040fd61 100644 --- a/source/common/config/utility.h +++ b/source/common/config/utility.h @@ -242,7 +242,6 @@ class Utility { /** * Translate a nested config into a proto message provided by the implementation factory. - * @param extension_name name of extension corresponding to config. * @param enclosing_message proto that contains a field 'config'. Note: the enclosing proto is * provided because for statically registered implementations, a custom config is generally * optional, which means the conversion must be done conditionally. @@ -269,6 +268,30 @@ class Utility { return config; } + /** + * Translate the typed any field into a proto message provided by the implementation factory. + * @param typed_config typed configuration. + * @param validation_visitor message validation visitor instance. + * @param factory implementation factory with the method 'createEmptyConfigProto' to produce a + * proto to be filled with the translated configuration. + */ + template + static ProtobufTypes::MessagePtr + translateAnyToFactoryConfig(const ProtobufWkt::Any& typed_config, + ProtobufMessage::ValidationVisitor& validation_visitor, + Factory& factory) { + ProtobufTypes::MessagePtr config = factory.createEmptyConfigProto(); + + // Fail in an obvious way if a plugin does not return a proto. + RELEASE_ASSERT(config != nullptr, ""); + + // Check that the config type is not google.protobuf.Empty + RELEASE_ASSERT(config->GetDescriptor()->full_name() != "google.protobuf.Empty", ""); + + translateOpaqueConfig(typed_config, ProtobufWkt::Struct(), validation_visitor, *config); + return config; + } + /** * Truncates the message to a length less than default GRPC trailers size limit (by default 8KiB). */ diff --git a/test/common/config/utility_test.cc b/test/common/config/utility_test.cc index 39befdc8f91e..9b3f7bf02cbd 100644 --- a/test/common/config/utility_test.cc +++ b/test/common/config/utility_test.cc @@ -12,6 +12,7 @@ #include "common/config/well_known_names.h" #include "common/protobuf/protobuf.h" +#include "test/mocks/config/mocks.h" #include "test/mocks/grpc/mocks.h" #include "test/mocks/local_info/mocks.h" #include "test/mocks/stats/mocks.h" @@ -287,6 +288,41 @@ TEST(UtilityTest, AnyWrongType) { R"(Unable to unpack as google.protobuf.Timestamp: \[type.googleapis.com/google.protobuf.Duration\] .*)"); } +TEST(UtilityTest, TranslateAnyWrongToFactoryConfig) { + ProtobufWkt::Duration source_duration; + source_duration.set_seconds(42); + ProtobufWkt::Any typed_config; + typed_config.PackFrom(source_duration); + + MockTypedFactory factory; + EXPECT_CALL(factory, createEmptyConfigProto()).WillOnce(Invoke([]() -> ProtobufTypes::MessagePtr { + return ProtobufTypes::MessagePtr{new ProtobufWkt::Timestamp()}; + })); + + EXPECT_THROW_WITH_REGEX( + Utility::translateAnyToFactoryConfig(typed_config, + ProtobufMessage::getStrictValidationVisitor(), factory), + EnvoyException, + R"(Unable to unpack as google.protobuf.Timestamp: \[type.googleapis.com/google.protobuf.Duration\] .*)"); +} + +TEST(UtilityTest, TranslateAnyToFactoryConfig) { + ProtobufWkt::Duration source_duration; + source_duration.set_seconds(42); + ProtobufWkt::Any typed_config; + typed_config.PackFrom(source_duration); + + MockTypedFactory factory; + EXPECT_CALL(factory, createEmptyConfigProto()).WillOnce(Invoke([]() -> ProtobufTypes::MessagePtr { + return ProtobufTypes::MessagePtr{new ProtobufWkt::Duration()}; + })); + + auto config = Utility::translateAnyToFactoryConfig( + typed_config, ProtobufMessage::getStrictValidationVisitor(), factory); + + EXPECT_THAT(*config, ProtoEq(source_duration)); +} + void packTypedStructIntoAny(ProtobufWkt::Any& typed_config, const Protobuf::Message& inner) { udpa::type::v1::TypedStruct typed_struct; (*typed_struct.mutable_type_url()) = diff --git a/test/mocks/config/mocks.cc b/test/mocks/config/mocks.cc index 0b8f8fc4784e..374eec10d071 100644 --- a/test/mocks/config/mocks.cc +++ b/test/mocks/config/mocks.cc @@ -39,5 +39,6 @@ MockSubscriptionCallbacks::MockSubscriptionCallbacks() { MockSubscriptionCallbacks::~MockSubscriptionCallbacks() = default; +MockTypedFactory::~MockTypedFactory() = default; } // namespace Config } // namespace Envoy diff --git a/test/mocks/config/mocks.h b/test/mocks/config/mocks.h index e7915fd3ece0..1df17c1ea903 100644 --- a/test/mocks/config/mocks.h +++ b/test/mocks/config/mocks.h @@ -5,6 +5,7 @@ #include "envoy/config/endpoint/v3/endpoint.pb.h" #include "envoy/config/grpc_mux.h" #include "envoy/config/subscription.h" +#include "envoy/config/typed_config.h" #include "envoy/service/discovery/v3/discovery.pb.h" #include "common/config/config_provider_impl.h" @@ -119,5 +120,15 @@ class MockConfigProviderManager : public ConfigProviderManager { const Envoy::Config::ConfigProviderManager::OptionalArg& optarg)); }; +class MockTypedFactory : public TypedFactory { +public: + ~MockTypedFactory() override; + + MOCK_METHOD(ProtobufTypes::MessagePtr, createEmptyConfigProto, ()); + MOCK_METHOD(std::string, configType, ()); + MOCK_METHOD(std::string, name, (), (const)); + MOCK_METHOD(std::string, category, (), (const)); +}; + } // namespace Config } // namespace Envoy