diff --git a/envoy/filter/config_provider_manager.h b/envoy/filter/config_provider_manager.h index 0f7a447d1029..158e7e158f80 100644 --- a/envoy/filter/config_provider_manager.h +++ b/envoy/filter/config_provider_manager.h @@ -23,7 +23,7 @@ using DynamicFilterConfigProviderPtr = std::unique_ptr class FilterConfigProviderManager { +template class FilterConfigProviderManager { public: virtual ~FilterConfigProviderManager() = default; @@ -40,7 +40,7 @@ template class FilterConfigProviderManager { */ virtual DynamicFilterConfigProviderPtr createDynamicFilterConfigProvider( const envoy::config::core::v3::ExtensionConfigSource& config_source, - const std::string& filter_config_name, Server::Configuration::FactoryContext& factory_context, + const std::string& filter_config_name, FactoryCtx& factory_context, const std::string& stat_prefix, bool last_filter_in_filter_chain, const std::string& filter_chain_type) PURE; diff --git a/source/common/filter/config_discovery_impl.cc b/source/common/filter/config_discovery_impl.cc index 226b0acf7e21..eb6eec5a0009 100644 --- a/source/common/filter/config_discovery_impl.cc +++ b/source/common/filter/config_discovery_impl.cc @@ -16,8 +16,6 @@ namespace Envoy { namespace Filter { -constexpr absl::string_view HttpStatPrefix = "http_filter."; - namespace { void validateTypeUrlHelper(const std::string& type_url, const absl::flat_hash_set require_type_urls) { @@ -232,41 +230,19 @@ void FilterConfigProviderManagerImplBase::applyLastOrDefaultConfig( } } -std::tuple HttpFilterConfigProviderManagerImpl::getMessage( - const envoy::config::core::v3::TypedExtensionConfig& filter_config, - Server::Configuration::ServerFactoryContext& factory_context) const { - auto& factory = - Config::Utility::getAndCheckFactory( - filter_config); - ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( - filter_config.typed_config(), - factory_context.messageValidationContext().dynamicValidationVisitor(), factory); - return {std::move(message), factory.name()}; -} - -absl::string_view HttpFilterConfigProviderManagerImpl::statPrefix() const { return HttpStatPrefix; } - -ProtobufTypes::MessagePtr HttpFilterConfigProviderManagerImpl::getDefaultConfig( - const ProtobufWkt::Any& proto_config, const std::string& filter_config_name, - Server::Configuration::FactoryContext& factory_context, bool last_filter_in_filter_chain, - const std::string& filter_chain_type, - const absl::flat_hash_set& require_type_urls) const { - auto* default_factory = - Config::Utility::getFactoryByType( - proto_config); - if (default_factory == nullptr) { +void FilterConfigProviderManagerImplBase::validateProtoConfigDefaultFactory( + const bool null_default_factory, const std::string& filter_config_name, + const std::string& type_url) const { + if (null_default_factory) { throw EnvoyException(fmt::format("Error: cannot find filter factory {} for default filter " "configuration with type URL {}.", - filter_config_name, proto_config.type_url())); + filter_config_name, type_url)); } - validateTypeUrlHelper(Config::Utility::getFactoryType(proto_config), require_type_urls); - ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( - proto_config, factory_context.messageValidationVisitor(), *default_factory); - Config::Utility::validateTerminalFilters( - filter_config_name, default_factory->name(), filter_chain_type, - default_factory->isTerminalFilterByProto(*message, factory_context), - last_filter_in_filter_chain); - return message; +} + +void FilterConfigProviderManagerImplBase::validateProtoConfigTypeUrl( + const std::string& type_url, const absl::flat_hash_set& require_type_urls) const { + validateTypeUrlHelper(type_url, require_type_urls); } } // namespace Filter diff --git a/source/common/filter/config_discovery_impl.h b/source/common/filter/config_discovery_impl.h index bdc46464c2aa..d30170e4b159 100644 --- a/source/common/filter/config_discovery_impl.h +++ b/source/common/filter/config_discovery_impl.h @@ -77,8 +77,7 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa absl::string_view stat_prefix) : DynamicFilterConfigProviderImplBase(subscription, require_type_urls, last_filter_in_filter_chain, filter_chain_type), - stat_prefix_(stat_prefix), factory_context_(factory_context), - main_config_(std::make_shared()), + stat_prefix_(stat_prefix), main_config_(std::make_shared()), default_configuration_(std::move(default_config)), tls_(factory_context.threadLocal()) { tls_.set([](Event::Dispatcher&) { return std::make_shared(); }); }; @@ -119,7 +118,6 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa protected: const std::string& getStatPrefix() const { return stat_prefix_; } - Server::Configuration::FactoryContext& getFactoryContext() const { return factory_context_; } private: virtual FactoryCb instantiateFilterFactory(const Protobuf::Message& message) const PURE; @@ -151,7 +149,6 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa }; const std::string stat_prefix_; - Server::Configuration::FactoryContext& factory_context_; std::shared_ptr main_config_; const ProtobufTypes::MessagePtr default_configuration_; ThreadLocal::TypedSlot tls_; @@ -161,13 +158,23 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa class HttpDynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImpl { public: - using DynamicFilterConfigProviderImpl::DynamicFilterConfigProviderImpl; + HttpDynamicFilterConfigProviderImpl(FilterConfigSubscriptionSharedPtr& subscription, + const absl::flat_hash_set& require_type_urls, + Server::Configuration::FactoryContext& factory_context, + ProtobufTypes::MessagePtr&& default_config, + bool last_filter_in_filter_chain, + const std::string& filter_chain_type, + absl::string_view stat_prefix) + : DynamicFilterConfigProviderImpl(subscription, require_type_urls, factory_context, + std::move(default_config), last_filter_in_filter_chain, + filter_chain_type, stat_prefix), + factory_context_(factory_context) {} void validateMessage(const std::string& config_name, const Protobuf::Message& message, const std::string& factory_name) const override { auto* factory = Registry::FactoryRegistry::getFactory( factory_name); - const bool is_terminal_filter = factory->isTerminalFilterByProto(message, getFactoryContext()); + const bool is_terminal_filter = factory->isTerminalFilterByProto(message, factory_context_); Config::Utility::validateTerminalFilters(config_name, factory_name, filter_chain_type_, is_terminal_filter, last_filter_in_filter_chain_); } @@ -176,7 +183,62 @@ class HttpDynamicFilterConfigProviderImpl Http::FilterFactoryCb instantiateFilterFactory(const Protobuf::Message& message) const override { auto* factory = Registry::FactoryRegistry:: getFactoryByType(message.GetTypeName()); - return factory->createFilterFactoryFromProto(message, getStatPrefix(), getFactoryContext()); + return factory->createFilterFactoryFromProto(message, getStatPrefix(), factory_context_); + } + + Server::Configuration::FactoryContext& factory_context_; +}; + +// Implementation of a listener dynamic filter config provider. +template +class ListenerDynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImpl { +public: + ListenerDynamicFilterConfigProviderImpl( + FilterConfigSubscriptionSharedPtr& subscription, + const absl::flat_hash_set& require_type_urls, + Server::Configuration::ListenerFactoryContext& factory_context, + ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain, + const std::string& filter_chain_type, absl::string_view stat_prefix) + : DynamicFilterConfigProviderImpl( + subscription, require_type_urls, factory_context, std::move(default_config), + last_filter_in_filter_chain, filter_chain_type, stat_prefix), + factory_context_(factory_context) {} + + void validateMessage(const std::string&, const Protobuf::Message&, + const std::string&) const override {} + +protected: + Server::Configuration::ListenerFactoryContext& factory_context_; +}; + +class TcpListenerDynamicFilterConfigProviderImpl + : public ListenerDynamicFilterConfigProviderImpl { +public: + using ListenerDynamicFilterConfigProviderImpl::ListenerDynamicFilterConfigProviderImpl; + +private: + Network::ListenerFilterFactoryCb + instantiateFilterFactory(const Protobuf::Message& message) const override { + auto* factory = + Registry::FactoryRegistry:: + getFactoryByType(message.GetTypeName()); + // TODO(yanjunxiang): Change nullptr to actual listener filter matcher. + return factory->createListenerFilterFactoryFromProto(message, nullptr, factory_context_); + } +}; + +class UdpListenerDynamicFilterConfigProviderImpl + : public ListenerDynamicFilterConfigProviderImpl { +public: + using ListenerDynamicFilterConfigProviderImpl::ListenerDynamicFilterConfigProviderImpl; + +private: + Network::UdpListenerFilterFactoryCb + instantiateFilterFactory(const Protobuf::Message& message) const override { + auto* factory = + Registry::FactoryRegistry:: + getFactoryByType(message.GetTypeName()); + return factory->createFilterFactoryFromProto(message, factory_context_); } }; @@ -295,6 +357,11 @@ class FilterConfigProviderManagerImplBase : Logger::Loggable void applyLastOrDefaultConfig(std::shared_ptr& subscription, DynamicFilterConfigProviderImplBase& provider, const std::string& filter_config_name); + void validateProtoConfigDefaultFactory(const bool null_default_factory, + const std::string& filter_config_name, + const std::string& type_url) const; + void validateProtoConfigTypeUrl(const std::string& type_url, + const absl::flat_hash_set& require_type_urls) const; private: absl::flat_hash_map> subscriptions_; @@ -304,14 +371,14 @@ class FilterConfigProviderManagerImplBase : Logger::Loggable /** * An implementation of FilterConfigProviderManager. */ -template +template class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBase, - public FilterConfigProviderManager, + public FilterConfigProviderManager, public Singleton::Instance { public: DynamicFilterConfigProviderPtr createDynamicFilterConfigProvider( const envoy::config::core::v3::ExtensionConfigSource& config_source, - const std::string& filter_config_name, Server::Configuration::FactoryContext& factory_context, + const std::string& filter_config_name, FactoryCtx& factory_context, const std::string& stat_prefix, bool last_filter_in_filter_chain, const std::string& filter_chain_type) override { std::string subscription_stat_prefix; @@ -367,54 +434,91 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa absl::string_view statPrefix() const override PURE; -protected: - virtual ProtobufTypes::MessagePtr - getDefaultConfig(const ProtobufWkt::Any& proto_config, const std::string& filter_config_name, - Server::Configuration::FactoryContext& factory_context, - bool last_filter_in_filter_chain, const std::string& filter_chain_type, - const absl::flat_hash_set& require_type_urls) const PURE; - -private: - virtual std::unique_ptr> - createFilterConfigProviderImpl(FilterConfigSubscriptionSharedPtr& subscription, - const absl::flat_hash_set& require_type_urls, - Server::Configuration::FactoryContext& factory_context, - ProtobufTypes::MessagePtr&& default_config, - bool last_filter_in_filter_chain, - const std::string& filter_chain_type, - absl::string_view stat_prefix) PURE; -}; - -class HttpFilterConfigProviderManagerImpl - : public FilterConfigProviderManagerImpl { -public: std::tuple getMessage(const envoy::config::core::v3::TypedExtensionConfig& filter_config, - Server::Configuration::ServerFactoryContext& factory_context) const override; - - absl::string_view statPrefix() const override; + Server::Configuration::ServerFactoryContext& factory_context) const override { + auto& factory = Config::Utility::getAndCheckFactory(filter_config); + ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( + filter_config.typed_config(), + factory_context.messageValidationContext().dynamicValidationVisitor(), factory); + return {std::move(message), factory.name()}; + } protected: + virtual void validateFilters(const std::string&, const std::string&, const std::string&, bool, + bool) const {}; + virtual bool isTerminalFilter(Factory*, Protobuf::Message&, FactoryCtx&) const { return false; } + ProtobufTypes::MessagePtr getDefaultConfig(const ProtobufWkt::Any& proto_config, const std::string& filter_config_name, - Server::Configuration::FactoryContext& factory_context, - bool last_filter_in_filter_chain, const std::string& filter_chain_type, - const absl::flat_hash_set& require_type_urls) const override; + FactoryCtx& factory_context, bool last_filter_in_filter_chain, + const std::string& filter_chain_type, + const absl::flat_hash_set& require_type_urls) const { + auto* default_factory = Config::Utility::getFactoryByType(proto_config); + validateProtoConfigDefaultFactory(default_factory == nullptr, filter_config_name, + proto_config.type_url()); + validateProtoConfigTypeUrl(Config::Utility::getFactoryType(proto_config), require_type_urls); + ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( + proto_config, factory_context.messageValidationVisitor(), *default_factory); + validateFilters(filter_config_name, default_factory->name(), filter_chain_type, + isTerminalFilter(default_factory, *message, factory_context), + last_filter_in_filter_chain); + return message; + } private: - std::unique_ptr> - createFilterConfigProviderImpl(FilterConfigSubscriptionSharedPtr& subscription, - const absl::flat_hash_set& require_type_urls, - Server::Configuration::FactoryContext& factory_context, - ProtobufTypes::MessagePtr&& default_config, - bool last_filter_in_filter_chain, - const std::string& filter_chain_type, - absl::string_view stat_prefix) override { - return std::make_unique( + std::unique_ptr> createFilterConfigProviderImpl( + FilterConfigSubscriptionSharedPtr& subscription, + const absl::flat_hash_set& require_type_urls, FactoryCtx& factory_context, + ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain, + const std::string& filter_chain_type, absl::string_view stat_prefix) { + return std::make_unique( subscription, require_type_urls, factory_context, std::move(default_config), last_filter_in_filter_chain, filter_chain_type, stat_prefix); } }; +// HTTP filter +class HttpFilterConfigProviderManagerImpl + : public FilterConfigProviderManagerImpl< + Server::Configuration::NamedHttpFilterConfigFactory, Http::FilterFactoryCb, + Server::Configuration::FactoryContext, HttpDynamicFilterConfigProviderImpl> { +public: + absl::string_view statPrefix() const override { return "http_filter."; } + +protected: + bool isTerminalFilter(Server::Configuration::NamedHttpFilterConfigFactory* default_factory, + Protobuf::Message& message, + Server::Configuration::FactoryContext& factory_context) const override { + return default_factory->isTerminalFilterByProto(message, factory_context); + } + void validateFilters(const std::string& filter_config_name, const std::string& filter_type, + const std::string& filter_chain_type, bool is_terminal_filter, + bool last_filter_in_filter_chain) const override { + Config::Utility::validateTerminalFilters(filter_config_name, filter_type, filter_chain_type, + is_terminal_filter, last_filter_in_filter_chain); + } +}; + +// TCP listener filter +class TcpListenerFilterConfigProviderManagerImpl + : public FilterConfigProviderManagerImpl< + Server::Configuration::NamedListenerFilterConfigFactory, Network::ListenerFilterFactoryCb, + Server::Configuration::ListenerFactoryContext, + TcpListenerDynamicFilterConfigProviderImpl> { +public: + absl::string_view statPrefix() const override { return "tcp_listener_filter."; } +}; + +// UDP listener filter +class UdpListenerFilterConfigProviderManagerImpl + : public FilterConfigProviderManagerImpl< + Server::Configuration::NamedUdpListenerFilterConfigFactory, + Network::UdpListenerFilterFactoryCb, Server::Configuration::ListenerFactoryContext, + UdpListenerDynamicFilterConfigProviderImpl> { +public: + absl::string_view statPrefix() const override { return "udp_listener_filter."; } +}; + } // namespace Filter } // namespace Envoy diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index 8d87d767a6da..8a0d1632086b 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -39,7 +39,9 @@ namespace Extensions { namespace NetworkFilters { namespace HttpConnectionManager { -using FilterConfigProviderManager = Filter::FilterConfigProviderManager; +using FilterConfigProviderManager = + Filter::FilterConfigProviderManager; /** * Config registration for the HTTP connection manager filter. @see NamedNetworkFilterConfigFactory. diff --git a/test/common/filter/BUILD b/test/common/filter/BUILD index 1429f044631e..6ddd0fba951e 100644 --- a/test/common/filter/BUILD +++ b/test/common/filter/BUILD @@ -17,6 +17,7 @@ envoy_cc_test( "//source/common/json:json_loader_lib", "//source/extensions/filters/http/router:config", "//test/integration/filters:add_body_filter_config_lib", + "//test/integration/filters:test_listener_filter_lib", "//test/mocks/local_info:local_info_mocks", "//test/mocks/protobuf:protobuf_mocks", "//test/mocks/server:server_mocks", diff --git a/test/common/filter/config_discovery_impl_test.cc b/test/common/filter/config_discovery_impl_test.cc index 0b725db42d85..6ae77285e58f 100644 --- a/test/common/filter/config_discovery_impl_test.cc +++ b/test/common/filter/config_discovery_impl_test.cc @@ -12,6 +12,9 @@ #include "source/common/filter/config_discovery_impl.h" #include "source/common/json/json_loader.h" +#include "test/integration/filters/add_body_filter.pb.h" +#include "test/integration/filters/test_listener_filter.h" +#include "test/integration/filters/test_listener_filter.pb.h" #include "test/mocks/init/mocks.h" #include "test/mocks/local_info/mocks.h" #include "test/mocks/protobuf/mocks.h" @@ -37,7 +40,7 @@ namespace Envoy { namespace Filter { namespace { -class FilterConfigDiscoveryTestBase : public testing::Test { +class FilterConfigDiscoveryTestBase { public: FilterConfigDiscoveryTestBase() { // For server_factory_context @@ -46,9 +49,9 @@ class FilterConfigDiscoveryTestBase : public testing::Test { .WillByDefault(ReturnRef(validation_context_)); ON_CALL(factory_context_.server_factory_context_, messageValidationContext()) .WillByDefault(ReturnRef(validation_context_)); - EXPECT_CALL(validation_context_, dynamicValidationVisitor()) - .WillRepeatedly(ReturnRef(validation_visitor_)); - EXPECT_CALL(factory_context_, initManager()).WillRepeatedly(ReturnRef(init_manager_)); + ON_CALL(validation_context_, dynamicValidationVisitor()) + .WillByDefault(ReturnRef(validation_visitor_)); + ON_CALL(factory_context_, initManager()).WillByDefault(ReturnRef(init_manager_)); ON_CALL(init_manager_, add(_)).WillByDefault(Invoke([this](const Init::Target& target) { init_target_handle_ = target.createHandle("test"); })); @@ -57,8 +60,11 @@ class FilterConfigDiscoveryTestBase : public testing::Test { [this](const Init::Watcher& watcher) { init_target_handle_->initialize(watcher); })); // Thread local storage assumes a single (main) thread with no workers. ON_CALL(factory_context_.admin_, concurrency()).WillByDefault(Return(0)); + + ON_CALL(factory_context_, listenerConfig()).WillByDefault(ReturnRef(listener_config_)); } + virtual ~FilterConfigDiscoveryTestBase() = default; Event::SimulatedTimeSystem& timeSystem() { return time_system_; } Event::SimulatedTimeSystem time_system_; @@ -69,49 +75,35 @@ class FilterConfigDiscoveryTestBase : public testing::Test { Init::ExpectableWatcherImpl init_watcher_; Init::TargetHandlePtr init_target_handle_; NiceMock scope_; + NiceMock listener_config_; }; -// Test base class with a single provider. +// Common base ECDS test class for HTTP filter, and TCP/UDP listener filter. +template class FilterConfigDiscoveryImplTest : public FilterConfigDiscoveryTestBase { public: FilterConfigDiscoveryImplTest() { - filter_config_provider_manager_ = std::make_unique(); + filter_config_provider_manager_ = std::make_unique(); } ~FilterConfigDiscoveryImplTest() override { factory_context_.thread_local_.shutdownThread(); } - DynamicFilterConfigProviderPtr - createProvider(std::string name, bool warm, bool default_configuration, - bool last_filter_config = true) { - + // Create listener filter config provider callbacks. + DynamicFilterConfigProviderPtr createProvider(std::string name, bool warm, + bool default_configuration, + bool last_filter_config = true) { EXPECT_CALL(init_manager_, add(_)); envoy::config::core::v3::ExtensionConfigSource config_source; - - std::string inject_default_configuration; - if (default_configuration) { - inject_default_configuration = R"EOF( -default_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - suppress_envoy_headers: true -)EOF"; - } - TestUtility::loadFromYaml(absl::Substitute(R"EOF( -config_source: { ads: {} } -$0 -type_urls: -- envoy.extensions.filters.http.router.v3.Router -)EOF", - inject_default_configuration), - config_source); - if (!warm) { - config_source.set_apply_default_config_without_warming(true); - TestUtility::loadFromYaml(R"EOF( -"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -)EOF", - *config_source.mutable_default_config()); + envoy::config::core::v3::AggregatedConfigSource ads; + config_source.mutable_config_source()->mutable_ads()->MergeFrom(ads); + config_source.add_type_urls(getTypeUrl()); + config_source.set_apply_default_config_without_warming(!warm); + if (default_configuration || !warm) { + ProtoType default_config; + config_source.mutable_default_config()->PackFrom(default_config); } return filter_config_provider_manager_->createDynamicFilterConfigProvider( - config_source, name, factory_context_, "xds.", last_filter_config, "http"); + config_source, name, factory_context_, "xds.", last_filter_config, getFilterType()); } void setup(bool warm = true, bool default_configuration = false, bool last_filter_config = true) { @@ -127,311 +119,362 @@ config_source: { ads: {} } init_manager_.initialize(init_watcher_); } - std::unique_ptr> + // Create a discovery response. + envoy::service::discovery::v3::DiscoveryResponse createResponse(std::string version, + std::string name) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info(version); + envoy::config::core::v3::TypedExtensionConfig extension_config; + extension_config.set_name(name); + extension_config.mutable_typed_config()->set_type_url("type.googleapis.com/" + getTypeUrl()); + response.add_resources()->PackFrom(extension_config); + return response; + } + + // Add a filter configuration by send a extension discovery response. Then removes it. + void incrementalTest() { + const auto response = createResponse("1", "foo"); + const auto decoded_resources = + TestUtility::decodeResources(response); + + EXPECT_CALL(init_watcher_, ready()); + callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()); + EXPECT_NE(absl::nullopt, provider_->config()); + EXPECT_EQ(1UL, scope_.counter(getConfigReloadCounter()).value()); + EXPECT_EQ(0UL, scope_.counter(getConfigFailCounter()).value()); + + // Ensure that we honor resource removals. + Protobuf::RepeatedPtrField remove; + *remove.Add() = "foo"; + callbacks_->onConfigUpdate({}, remove, "1"); + EXPECT_EQ(2UL, scope_.counter(getConfigReloadCounter()).value()); + EXPECT_EQ(0UL, scope_.counter(getConfigFailCounter()).value()); + } + + virtual const std::string getTypeUrl() const PURE; + virtual const std::string getFilterType() const PURE; + virtual const std::string getConfigReloadCounter() const PURE; + virtual const std::string getConfigFailCounter() const PURE; + + std::unique_ptr> filter_config_provider_manager_; - DynamicFilterConfigProviderPtr provider_; + DynamicFilterConfigProviderPtr provider_; Config::SubscriptionCallbacks* callbacks_{}; }; -TEST_F(FilterConfigDiscoveryImplTest, DestroyReady) { - setup(); - EXPECT_CALL(init_watcher_, ready()); +// HTTP filter test +class HttpFilterConfigDiscoveryImplTest + : public FilterConfigDiscoveryImplTest { +public: + const std::string getTypeUrl() const override { + return "envoy.extensions.filters.http.router.v3.Router"; + } + const std::string getFilterType() const override { return "http"; } + const std::string getConfigReloadCounter() const override { + return "extension_config_discovery.http_filter.foo.config_reload"; + } + const std::string getConfigFailCounter() const override { + return "extension_config_discovery.http_filter.foo.config_fail"; + } +}; + +// TCP listener filter test +class TcpListenerFilterConfigDiscoveryImplTest + : public FilterConfigDiscoveryImplTest< + Network::ListenerFilterFactoryCb, Server::Configuration::ListenerFactoryContext, + TcpListenerFilterConfigProviderManagerImpl, Envoy::ProtobufWkt::Struct> { +public: + const std::string getTypeUrl() const override { return "google.protobuf.Struct"; } + const std::string getFilterType() const override { return "listener"; } + const std::string getConfigReloadCounter() const override { + return "extension_config_discovery.tcp_listener_filter.foo.config_reload"; + } + const std::string getConfigFailCounter() const override { + return "extension_config_discovery.tcp_listener_filter.foo.config_fail"; + } +}; + +// UDP listener filter test +class UdpListenerFilterConfigDiscoveryImplTest + : public FilterConfigDiscoveryImplTest< + Network::UdpListenerFilterFactoryCb, Server::Configuration::ListenerFactoryContext, + UdpListenerFilterConfigProviderManagerImpl, + test::integration::filters::TestUdpListenerFilterConfig> { +public: + const std::string getTypeUrl() const override { + return "test.integration.filters.TestUdpListenerFilterConfig"; + } + const std::string getFilterType() const override { return "listener"; } + const std::string getConfigReloadCounter() const override { + return "extension_config_discovery.udp_listener_filter.foo.config_reload"; + } + const std::string getConfigFailCounter() const override { + return "extension_config_discovery.udp_listener_filter.foo.config_fail"; + } +}; + +/*************************************************************************************** + * Parameterized test for * + * HTTP filter, TCP listener filter And UDP listener filter * + * * + ***************************************************************************************/ +template +class FilterConfigDiscoveryImplTestParameter : public testing::Test {}; + +// The test filter types. +using FilterConfigDiscoveryTestTypes = + ::testing::Types; + +TYPED_TEST_SUITE(FilterConfigDiscoveryImplTestParameter, FilterConfigDiscoveryTestTypes); + +// TYPED_TEST will run the same test for each of the above filter type. +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, DestroyReady) { + TypeParam config_discovery_test; + config_discovery_test.setup(); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); } -TEST_F(FilterConfigDiscoveryImplTest, Basic) { +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, Basic) { InSequence s; - setup(); - EXPECT_EQ("foo", provider_->name()); - EXPECT_EQ(absl::nullopt, provider_->config()); + TypeParam config_discovery_test; + config_discovery_test.setup(); + EXPECT_EQ("foo", config_discovery_test.provider_->name()); + EXPECT_EQ(absl::nullopt, config_discovery_test.provider_->config()); // Initial request. { - const std::string response_yaml = R"EOF( - version_info: "1" - resources: - - "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - )EOF"; - const auto response = - TestUtility::parseYaml(response_yaml); + const auto response = config_discovery_test.createResponse("1", "foo"); const auto decoded_resources = TestUtility::decodeResources(response); - EXPECT_CALL(init_watcher_, ready()); - callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()); - EXPECT_NE(absl::nullopt, provider_->config()); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); + config_discovery_test.callbacks_->onConfigUpdate(decoded_resources.refvec_, + response.version_info()); + EXPECT_NE(absl::nullopt, config_discovery_test.provider_->config()); EXPECT_EQ(1UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); - EXPECT_EQ(0UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_fail").value()); + config_discovery_test.scope_.counter(config_discovery_test.getConfigReloadCounter()) + .value()); + EXPECT_EQ( + 0UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigFailCounter()).value()); } // 2nd request with same response. Based on hash should not reload config. { - const std::string response_yaml = R"EOF( - version_info: "2" - resources: - - "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - )EOF"; - const auto response = - TestUtility::parseYaml(response_yaml); + const auto response = config_discovery_test.createResponse("2", "foo"); const auto decoded_resources = TestUtility::decodeResources(response); - callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()); + config_discovery_test.callbacks_->onConfigUpdate(decoded_resources.refvec_, + response.version_info()); + EXPECT_EQ(1UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); - EXPECT_EQ(0UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_fail").value()); + config_discovery_test.scope_.counter(config_discovery_test.getConfigReloadCounter()) + .value()); + EXPECT_EQ( + 0UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigFailCounter()).value()); } } -TEST_F(FilterConfigDiscoveryImplTest, BasicDeprecatedStatPrefix) { +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, BasicDeprecatedStatPrefix) { TestScopedRuntime scoped_runtime; scoped_runtime.mergeValues({{"envoy.reloadable_features.top_level_ecds_stats", "false"}}); InSequence s; - setup(); - EXPECT_EQ("foo", provider_->name()); - EXPECT_EQ(absl::nullopt, provider_->config()); + TypeParam config_discovery_test; + config_discovery_test.setup(); + EXPECT_EQ("foo", config_discovery_test.provider_->name()); + EXPECT_EQ(absl::nullopt, config_discovery_test.provider_->config()); - const std::string response_yaml = R"EOF( - version_info: "1" - resources: - - "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - )EOF"; - const auto response = - TestUtility::parseYaml(response_yaml); + const auto response = config_discovery_test.createResponse("1", "foo"); const auto decoded_resources = TestUtility::decodeResources(response); - EXPECT_CALL(init_watcher_, ready()); - callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()); - EXPECT_NE(absl::nullopt, provider_->config()); - EXPECT_EQ(1UL, scope_.counter("xds.extension_config_discovery.foo.config_reload").value()); - EXPECT_EQ(0UL, scope_.counter("xds.extension_config_discovery.foo.config_fail").value()); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); + config_discovery_test.callbacks_->onConfigUpdate(decoded_resources.refvec_, + response.version_info()); + EXPECT_NE(absl::nullopt, config_discovery_test.provider_->config()); + EXPECT_EQ(1UL, + config_discovery_test.scope_.counter("xds.extension_config_discovery.foo.config_reload") + .value()); + EXPECT_EQ(0UL, + config_discovery_test.scope_.counter("xds.extension_config_discovery.foo.config_fail") + .value()); } -TEST_F(FilterConfigDiscoveryImplTest, ConfigFailed) { +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, ConfigFailed) { InSequence s; - setup(); - EXPECT_CALL(init_watcher_, ready()); - callbacks_->onConfigUpdateFailed(Config::ConfigUpdateFailureReason::FetchTimedout, {}); - EXPECT_EQ(0UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); - EXPECT_EQ(1UL, scope_.counter("extension_config_discovery.http_filter.foo.config_fail").value()); + TypeParam config_discovery_test; + config_discovery_test.setup(); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); + config_discovery_test.callbacks_->onConfigUpdateFailed( + Config::ConfigUpdateFailureReason::FetchTimedout, {}); + EXPECT_EQ( + 0UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigReloadCounter()).value()); + EXPECT_EQ( + 1UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigFailCounter()).value()); } -TEST_F(FilterConfigDiscoveryImplTest, TooManyResources) { +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, TooManyResources) { InSequence s; - setup(); - const std::string response_yaml = R"EOF( - version_info: "1" - resources: - - "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - )EOF"; - const auto response = - TestUtility::parseYaml(response_yaml); + TypeParam config_discovery_test; + config_discovery_test.setup(); + auto response = config_discovery_test.createResponse("1", "foo"); + envoy::config::core::v3::TypedExtensionConfig extension_config; + extension_config.set_name("foo"); + extension_config.mutable_typed_config()->set_type_url("type.googleapis.com/" + + config_discovery_test.getTypeUrl()); + response.add_resources()->PackFrom(extension_config); const auto decoded_resources = TestUtility::decodeResources(response); - EXPECT_CALL(init_watcher_, ready()); - EXPECT_THROW_WITH_MESSAGE( - callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()), - EnvoyException, "Unexpected number of resources in ExtensionConfigDS response: 2"); - EXPECT_EQ(0UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); + EXPECT_THROW_WITH_MESSAGE(config_discovery_test.callbacks_->onConfigUpdate( + decoded_resources.refvec_, response.version_info()), + EnvoyException, + "Unexpected number of resources in ExtensionConfigDS response: 2"); + EXPECT_EQ( + 0UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigReloadCounter()).value()); } -TEST_F(FilterConfigDiscoveryImplTest, WrongName) { +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, WrongName) { InSequence s; - setup(); - const std::string response_yaml = R"EOF( - version_info: "1" - resources: - - "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: bar - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - )EOF"; - const auto response = - TestUtility::parseYaml(response_yaml); + TypeParam config_discovery_test; + config_discovery_test.setup(); + const auto response = config_discovery_test.createResponse("1", "bar"); const auto decoded_resources = TestUtility::decodeResources(response); - EXPECT_CALL(init_watcher_, ready()); - EXPECT_THROW_WITH_MESSAGE( - callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()), - EnvoyException, "Unexpected resource name in ExtensionConfigDS response: bar"); - EXPECT_EQ(0UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); + EXPECT_THROW_WITH_MESSAGE(config_discovery_test.callbacks_->onConfigUpdate( + decoded_resources.refvec_, response.version_info()), + EnvoyException, + "Unexpected resource name in ExtensionConfigDS response: bar"); + EXPECT_EQ( + 0UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigReloadCounter()).value()); } -TEST_F(FilterConfigDiscoveryImplTest, Incremental) { +// Without default config. +// First adding a filter configuration by send a extension discovery response, then removes it. +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, IncrementalWithOutDefault) { InSequence s; - setup(); - - // Bool parameter allows generating two different filter configs. - auto do_xds_response = [this](bool b) { - const std::string response_yaml = fmt::format(R"EOF( -version_info: "1" -resources: -- "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - suppress_envoy_headers: {} -)EOF", - b); - const auto response = - TestUtility::parseYaml(response_yaml); - const auto decoded_resources = - TestUtility::decodeResources(response); - callbacks_->onConfigUpdate(decoded_resources.refvec_, {}, response.version_info()); - }; - - EXPECT_CALL(init_watcher_, ready()); - do_xds_response(true); - EXPECT_NE(absl::nullopt, provider_->config()); - EXPECT_EQ(1UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); - EXPECT_EQ(0UL, scope_.counter("extension_config_discovery.http_filter.foo.config_fail").value()); - - // Ensure that we honor resource removals. - Protobuf::RepeatedPtrField remove; - *remove.Add() = "foo"; - callbacks_->onConfigUpdate({}, remove, "1"); - EXPECT_EQ(absl::nullopt, provider_->config()); - EXPECT_EQ(2UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); - EXPECT_EQ(0UL, scope_.counter("extension_config_discovery.http_filter.foo.config_fail").value()); + TypeParam config_discovery_test; + config_discovery_test.setup(); + config_discovery_test.incrementalTest(); + // Verify the provider config is empty. + EXPECT_EQ(absl::nullopt, config_discovery_test.provider_->config()); } -TEST_F(FilterConfigDiscoveryImplTest, IncrementalWithDefault) { +// With default config. +// First adding a filter configuration by send a extension discovery response, then removes it. +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, IncrementalWithDefault) { InSequence s; - setup(true, true); - - // Bool parameter allows generating two different filter configs. The default configuration has - // b=true. - auto do_xds_response = [this](bool b) { - const std::string response_yaml = fmt::format(R"EOF( -version_info: "1" -resources: -- "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - suppress_envoy_headers: {} -)EOF", - b); - - const auto response = - TestUtility::parseYaml(response_yaml); - const auto decoded_resources = - TestUtility::decodeResources(response); - callbacks_->onConfigUpdate(decoded_resources.refvec_, {}, response.version_info()); - }; - - EXPECT_NE(absl::nullopt, provider_->config()); - - EXPECT_CALL(init_watcher_, ready()); - do_xds_response(false); - EXPECT_NE(absl::nullopt, provider_->config()); - EXPECT_EQ(1UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); - EXPECT_EQ(0UL, scope_.counter("extension_config_discovery.http_filter.foo.config_fail").value()); - - // If we get a removal while a default is configured, we should revert back to the default - // instead of clearing out the factory. - Protobuf::RepeatedPtrField remove; - *remove.Add() = "foo"; - callbacks_->onConfigUpdate({}, remove, "1"); - EXPECT_NE(absl::nullopt, provider_->config()); - EXPECT_EQ(2UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); - EXPECT_EQ(0UL, scope_.counter("extension_config_discovery.http_filter.foo.config_fail").value()); + TypeParam config_discovery_test; + config_discovery_test.setup(true, true); + config_discovery_test.incrementalTest(); + // Verify the provider config is not empty since the default config is there. + EXPECT_NE(absl::nullopt, config_discovery_test.provider_->config()); } -TEST_F(FilterConfigDiscoveryImplTest, ApplyWithoutWarming) { +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, ApplyWithoutWarming) { InSequence s; - setup(false); - EXPECT_EQ("foo", provider_->name()); - EXPECT_NE(absl::nullopt, provider_->config()); - EXPECT_EQ(0UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); - EXPECT_EQ(0UL, scope_.counter("extension_config_discovery.http_filter.foo.config_fail").value()); + TypeParam config_discovery_test; + config_discovery_test.setup(false); + EXPECT_EQ("foo", config_discovery_test.provider_->name()); + EXPECT_NE(absl::nullopt, config_discovery_test.provider_->config()); + EXPECT_EQ( + 0UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigReloadCounter()).value()); + EXPECT_EQ( + 0UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigFailCounter()).value()); } -TEST_F(FilterConfigDiscoveryImplTest, DualProviders) { +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, DualProviders) { InSequence s; - setup(); - auto provider2 = createProvider("foo", true, false); + TypeParam config_discovery_test; + config_discovery_test.setup(); + const auto provider2 = config_discovery_test.createProvider("foo", true, false); EXPECT_EQ("foo", provider2->name()); EXPECT_EQ(absl::nullopt, provider2->config()); - const std::string response_yaml = R"EOF( - version_info: "1" - resources: - - "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - )EOF"; - const auto response = - TestUtility::parseYaml(response_yaml); + const auto response = config_discovery_test.createResponse("1", "foo"); const auto decoded_resources = TestUtility::decodeResources(response); - EXPECT_CALL(init_watcher_, ready()); - callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()); - EXPECT_NE(absl::nullopt, provider_->config()); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); + config_discovery_test.callbacks_->onConfigUpdate(decoded_resources.refvec_, + response.version_info()); + EXPECT_NE(absl::nullopt, config_discovery_test.provider_->config()); EXPECT_NE(absl::nullopt, provider2->config()); - EXPECT_EQ(1UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); + EXPECT_EQ( + 1UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigReloadCounter()).value()); } -TEST_F(FilterConfigDiscoveryImplTest, DualProvidersInvalid) { +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, DualProvidersInvalid) { InSequence s; - setup(); - auto provider2 = createProvider("foo", true, false); - const std::string response_yaml = R"EOF( - version_info: "1" - resources: - - "@type": type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig - name: foo - typed_config: - "@type": type.googleapis.com/test.integration.filters.AddBodyFilterConfig - body_size: 10 - )EOF"; - const auto response = - TestUtility::parseYaml(response_yaml); + TypeParam config_discovery_test; + config_discovery_test.setup(); + const auto provider2 = config_discovery_test.createProvider("foo", true, false); + + // Create a response with a random type AddBodyFilterConfig not matching with providers. + auto add_body_filter_config = test::integration::filters::AddBodyFilterConfig(); + add_body_filter_config.set_body_size(10); + envoy::config::core::v3::TypedExtensionConfig extension_config; + extension_config.set_name("foo"); + extension_config.mutable_typed_config()->PackFrom(add_body_filter_config); + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info("1"); + response.add_resources()->PackFrom(extension_config); + const auto decoded_resources = TestUtility::decodeResources(response); - EXPECT_CALL(init_watcher_, ready()); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); EXPECT_THROW_WITH_MESSAGE( - callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()), + config_discovery_test.callbacks_->onConfigUpdate(decoded_resources.refvec_, + response.version_info()), EnvoyException, "Error: filter config has type URL test.integration.filters.AddBodyFilterConfig but " - "expect envoy.extensions.filters.http.router.v3.Router."); - EXPECT_EQ(0UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); + "expect " + + config_discovery_test.getTypeUrl() + "."); + EXPECT_EQ( + 0UL, + config_discovery_test.scope_.counter(config_discovery_test.getConfigReloadCounter()).value()); +} + +// Throw Envoy exception when default config is wrong. +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, WrongDefaultConfig) { + InSequence s; + TypeParam config_discovery_test; + envoy::config::core::v3::ExtensionConfigSource config_source; + // Set up the default config with a bogus type url. + config_source.mutable_default_config()->set_type_url( + "type.googleapis.com/test.integration.filters.Bogus"); + EXPECT_THROW_WITH_MESSAGE( + config_discovery_test.filter_config_provider_manager_->createDynamicFilterConfigProvider( + config_source, "foo", config_discovery_test.factory_context_, "xds.", true, + config_discovery_test.getFilterType()), + EnvoyException, + "Error: cannot find filter factory foo for default filter " + "configuration with type URL " + "type.googleapis.com/test.integration.filters.Bogus."); } // Raise exception when filter is not the last filter in filter chain, but the filter is terminal -// filter. -TEST_F(FilterConfigDiscoveryImplTest, TerminalFilterInvalid) { +// filter. This test is HTTP filter specific. +TYPED_TEST(FilterConfigDiscoveryImplTestParameter, TerminalFilterInvalid) { InSequence s; - setup(true, false, false); + TypeParam config_discovery_test; + if (config_discovery_test.getFilterType() != "http") { + return; + } + + config_discovery_test.setup(true, false, false); const std::string response_yaml = R"EOF( version_info: "1" resources: @@ -444,14 +487,16 @@ TEST_F(FilterConfigDiscoveryImplTest, TerminalFilterInvalid) { TestUtility::parseYaml(response_yaml); const auto decoded_resources = TestUtility::decodeResources(response); - EXPECT_CALL(init_watcher_, ready()); + EXPECT_CALL(config_discovery_test.init_watcher_, ready()); EXPECT_THROW_WITH_MESSAGE( - callbacks_->onConfigUpdate(decoded_resources.refvec_, response.version_info()), + config_discovery_test.callbacks_->onConfigUpdate(decoded_resources.refvec_, + response.version_info()), EnvoyException, "Error: terminal filter named foo of type envoy.filters.http.router must be the last filter " "in a http filter chain."); EXPECT_EQ(0UL, - scope_.counter("extension_config_discovery.http_filter.foo.config_reload").value()); + config_discovery_test.scope_.counter("xds.extension_config_discovery.foo.config_reload") + .value()); } } // namespace diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 2208f0745cbf..ea7b4aad601e 100644 --- a/test/integration/filters/BUILD +++ b/test/integration/filters/BUILD @@ -14,6 +14,11 @@ envoy_proto_library( srcs = ["add_body_filter.proto"], ) +envoy_proto_library( + name = "test_listener_filter_proto", + srcs = ["test_listener_filter.proto"], +) + envoy_cc_test_library( name = "test_listener_filter_lib", srcs = [ @@ -23,6 +28,7 @@ envoy_cc_test_library( "test_listener_filter.h", ], deps = [ + ":test_listener_filter_proto_cc_proto", "//envoy/network:filter_interface", "//envoy/registry", "//envoy/server:filter_config_interface", diff --git a/test/integration/filters/test_listener_filter.cc b/test/integration/filters/test_listener_filter.cc index e4b6b01a5489..0951083c6f8e 100644 --- a/test/integration/filters/test_listener_filter.cc +++ b/test/integration/filters/test_listener_filter.cc @@ -1,5 +1,7 @@ #include "test/integration/filters/test_listener_filter.h" +#include "test/integration/filters/test_listener_filter.pb.h" + namespace Envoy { /** @@ -28,7 +30,32 @@ class TestInspectorConfigFactory : public Server::Configuration::NamedListenerFi absl::Mutex TestListenerFilter::alpn_lock_; std::string TestListenerFilter::alpn_; +/** + * Config registration for the UDP test filter. + */ +class TestUdpInspectorConfigFactory + : public Server::Configuration::NamedUdpListenerFilterConfigFactory { +public: + // NamedUdpListenerFilterConfigFactory + Network::UdpListenerFilterFactoryCb + createFilterFactoryFromProto(const Protobuf::Message&, + Server::Configuration::ListenerFactoryContext&) override { + return [](Network::UdpListenerFilterManager& filter_manager, + Network::UdpReadFilterCallbacks& callbacks) -> void { + filter_manager.addReadFilter(std::make_unique(callbacks)); + }; + } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } + + std::string name() const override { return "envoy.filters.udp_listener.test"; } +}; + REGISTER_FACTORY(TestInspectorConfigFactory, Server::Configuration::NamedListenerFilterConfigFactory){"envoy.listener.test"}; +REGISTER_FACTORY(TestUdpInspectorConfigFactory, + Server::Configuration::NamedUdpListenerFilterConfigFactory){"envoy.listener.test"}; } // namespace Envoy diff --git a/test/integration/filters/test_listener_filter.h b/test/integration/filters/test_listener_filter.h index aae0424aac06..dfb71bae40cd 100644 --- a/test/integration/filters/test_listener_filter.h +++ b/test/integration/filters/test_listener_filter.h @@ -32,4 +32,21 @@ class TestListenerFilter : public Network::ListenerFilter { static std::string alpn_; }; +/** + * Test UDP listener filter. + */ +class TestUdpListenerFilter : public Network::UdpListenerReadFilter { +public: + TestUdpListenerFilter(Network::UdpReadFilterCallbacks& callbacks) + : UdpListenerReadFilter(callbacks) {} + + // Network::UdpListenerReadFilter callbacks + Network::FilterStatus onData(Network::UdpRecvData&) override { + return Network::FilterStatus::Continue; + } + Network::FilterStatus onReceiveError(Api::IoError::IoErrorCode) override { + return Network::FilterStatus::Continue; + } +}; + } // namespace Envoy diff --git a/test/integration/filters/test_listener_filter.proto b/test/integration/filters/test_listener_filter.proto new file mode 100644 index 000000000000..51ae9d14142e --- /dev/null +++ b/test/integration/filters/test_listener_filter.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package test.integration.filters; + +// Configuration for UDP listener filter test +message TestUdpListenerFilterConfig { +} diff --git a/test/mocks/server/factory_context.h b/test/mocks/server/factory_context.h index 79797209d211..883bb82a3144 100644 --- a/test/mocks/server/factory_context.h +++ b/test/mocks/server/factory_context.h @@ -16,7 +16,7 @@ namespace Envoy { namespace Server { namespace Configuration { -class MockFactoryContext : public virtual FactoryContext { +class MockFactoryContext : public virtual ListenerFactoryContext { public: MockFactoryContext(); ~MockFactoryContext() override; @@ -45,6 +45,9 @@ class MockFactoryContext : public virtual FactoryContext { MOCK_METHOD(const Envoy::Config::TypedMetadata&, listenerTypedMetadata, (), (const)); MOCK_METHOD(envoy::config::core::v3::TrafficDirection, direction, (), (const)); MOCK_METHOD(TimeSource&, timeSource, ()); + + MOCK_METHOD(const Network::ListenerConfig&, listenerConfig, (), (const)); + Event::TestTimeSystem& timeSystem() { return time_system_; } Grpc::Context& grpcContext() override { return grpc_context_; } Http::Context& httpContext() override { return http_context_; }