Skip to content

Commit

Permalink
ecds: templatize FilterConfigProviderManagerImpl (#18832)
Browse files Browse the repository at this point in the history
Part of #14696 (comment). The refactoring that enables this is mainly within `FilterConfigSubscription::onConfigUpdate`. The rest is adding and removing template parameters.
Risk Level: Low
Testing: Existing (refactoring)
Docs Changes: N/A
Release Notes: N/A
Platform Specific Features: N/A
#14696

Signed-off-by: Taylor Barrella <[email protected]>
  • Loading branch information
tbarrella authored Nov 5, 2021
1 parent 26da599 commit 0264041
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 102 deletions.
6 changes: 3 additions & 3 deletions envoy/config/extension_config_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ using ConfigAppliedCb = std::function<void()>;
* the extension configuration discovery service. Dynamically updated extension
* configurations may share subscriptions across extension config providers.
*/
template <class Factory, class FactoryCallback> class ExtensionConfigProvider {
template <class FactoryCallback> class ExtensionConfigProvider {
public:
virtual ~ExtensionConfigProvider() = default;

Expand Down Expand Up @@ -63,9 +63,9 @@ class DynamicExtensionConfigProviderBase {
virtual void applyDefaultConfiguration() PURE;
};

template <class Factory, class FactoryCallback>
template <class FactoryCallback>
class DynamicExtensionConfigProvider : public DynamicExtensionConfigProviderBase,
public ExtensionConfigProvider<Factory, FactoryCallback> {};
public ExtensionConfigProvider<FactoryCallback> {};

} // namespace Config
} // namespace Envoy
1 change: 0 additions & 1 deletion envoy/filter/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ envoy_cc_library(
hdrs = ["config_provider_manager.h"],
deps = [
"//envoy/config:extension_config_provider_interface",
"//envoy/http:filter_interface",
"//envoy/init:manager_interface",
"//envoy/server:filter_config_interface",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
Expand Down
24 changes: 12 additions & 12 deletions envoy/filter/config_provider_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include "envoy/config/core/v3/config_source.pb.h"
#include "envoy/config/extension_config_provider.h"
#include "envoy/http/filter.h"
#include "envoy/init/manager.h"
#include "envoy/server/filter_config.h"

Expand All @@ -11,19 +10,20 @@
namespace Envoy {
namespace Filter {

using FilterConfigProvider =
Envoy::Config::ExtensionConfigProvider<Server::Configuration::NamedHttpFilterConfigFactory,
Envoy::Http::FilterFactoryCb>;
using FilterConfigProviderPtr = std::unique_ptr<FilterConfigProvider>;
using DynamicFilterConfigProvider = Envoy::Config::DynamicExtensionConfigProvider<
Server::Configuration::NamedHttpFilterConfigFactory, Envoy::Http::FilterFactoryCb>;
using DynamicFilterConfigProviderPtr = std::unique_ptr<DynamicFilterConfigProvider>;
template <class FactoryCb>
using FilterConfigProvider = Envoy::Config::ExtensionConfigProvider<FactoryCb>;
template <class FactoryCb>
using FilterConfigProviderPtr = std::unique_ptr<FilterConfigProvider<FactoryCb>>;
template <class FactoryCb>
using DynamicFilterConfigProvider = Envoy::Config::DynamicExtensionConfigProvider<FactoryCb>;
template <class FactoryCb>
using DynamicFilterConfigProviderPtr = std::unique_ptr<DynamicFilterConfigProvider<FactoryCb>>;

/**
* The FilterConfigProviderManager exposes the ability to get an FilterConfigProvider
* for both static and dynamic filter config providers.
*/
class FilterConfigProviderManager {
template <class FactoryCb> class FilterConfigProviderManager {
public:
virtual ~FilterConfigProviderManager() = default;

Expand All @@ -38,7 +38,7 @@ class FilterConfigProviderManager {
* configured chain
* @param filter_chain_type is the filter chain type
*/
virtual DynamicFilterConfigProviderPtr createDynamicFilterConfigProvider(
virtual DynamicFilterConfigProviderPtr<FactoryCb> createDynamicFilterConfigProvider(
const envoy::config::core::v3::ExtensionConfigSource& config_source,
const std::string& filter_config_name, Server::Configuration::FactoryContext& factory_context,
const std::string& stat_prefix, bool last_filter_in_filter_chain,
Expand All @@ -49,8 +49,8 @@ class FilterConfigProviderManager {
* @param config is a fully resolved filter instantiation factory.
* @param filter_config_name is the name of the filter configuration resource.
*/
virtual FilterConfigProviderPtr
createStaticFilterConfigProvider(const Envoy::Http::FilterFactoryCb& config,
virtual FilterConfigProviderPtr<FactoryCb>
createStaticFilterConfigProvider(const FactoryCb& config,
const std::string& filter_config_name) PURE;
};

Expand Down
69 changes: 18 additions & 51 deletions source/common/filter/config_discovery_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ FilterConfigSubscription::FilterConfigSubscription(
: Config::SubscriptionBase<envoy::config::core::v3::TypedExtensionConfig>(
factory_context.messageValidationContext().dynamicValidationVisitor(), "name"),
filter_config_name_(filter_config_name), factory_context_(factory_context),
validator_(factory_context.messageValidationContext().dynamicValidationVisitor()),
init_target_(fmt::format("FilterConfigSubscription init {}", filter_config_name_),
[this]() { start(); }),
scope_(factory_context.scope().createScope(stat_prefix + "extension_config_discovery." +
Expand Down Expand Up @@ -116,36 +115,32 @@ void FilterConfigSubscription::onConfigUpdate(
if (new_hash == last_config_hash_) {
return;
}
auto& factory =
Config::Utility::getAndCheckFactory<Server::Configuration::NamedHttpFilterConfigFactory>(
filter_config);
// Ensure that the filter config is valid in the filter chain context once the proto is processed.
// Validation happens before updating to prevent a partial update application. It might be
// possible that the providers have distinct type URL constraints.
const auto type_url = Config::Utility::getFactoryType(filter_config.typed_config());
for (auto* provider : filter_config_providers_) {
provider->validateTypeUrl(type_url);
}
ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
filter_config.typed_config(), validator_, factory);
bool is_terminal_filter = factory.isTerminalFilterByProto(*message, factory_context_);
auto [message, factory_name, is_terminal_filter] =
filter_config_provider_manager_.getMessage(filter_config, factory_context_);
for (auto* provider : filter_config_providers_) {
provider->validateTerminalFilter(filter_config_name_, factory.name(), is_terminal_filter);
provider->validateTerminalFilter(filter_config_name_, factory_name, is_terminal_filter);
}
ENVOY_LOG(debug, "Updating filter config {}", filter_config_name_);

Common::applyToAllWithCleanup<DynamicFilterConfigProviderImplBase*>(
filter_config_providers_,
[&message, &version_info](DynamicFilterConfigProviderImplBase* provider,
std::shared_ptr<Cleanup> cleanup) {
[&message = message, &version_info](DynamicFilterConfigProviderImplBase* provider,
std::shared_ptr<Cleanup> cleanup) {
provider->onConfigUpdate(*message, version_info, [cleanup] {});
},
[this]() { stats_.config_reload_.inc(); });
last_config_hash_ = new_hash;
last_config_ = std::move(message);
last_type_url_ = type_url;
last_version_info_ = version_info;
last_filter_name_ = factory.name();
last_filter_name_ = factory_name;
last_filter_is_terminal_ = is_terminal_filter;
}

Expand Down Expand Up @@ -244,46 +239,18 @@ void FilterConfigProviderManagerImplBase::applyLastOrDefaultConfig(
}
}

DynamicFilterConfigProviderPtr FilterConfigProviderManagerImpl::createDynamicFilterConfigProvider(
const envoy::config::core::v3::ExtensionConfigSource& config_source,
const std::string& filter_config_name, Server::Configuration::FactoryContext& factory_context,
const std::string& stat_prefix, bool last_filter_in_filter_chain,
const std::string& filter_chain_type) {
auto subscription = getSubscription(config_source.config_source(), filter_config_name,
factory_context, stat_prefix);
// For warming, wait until the subscription receives the first response to indicate readiness.
// Otherwise, mark ready immediately and start the subscription on initialization. A default
// config is expected in the latter case.
if (!config_source.apply_default_config_without_warming()) {
factory_context.initManager().add(subscription->initTarget());
}
absl::flat_hash_set<std::string> require_type_urls;
for (const auto& type_url : config_source.type_urls()) {
auto factory_type_url = TypeUtil::typeUrlToDescriptorFullName(type_url);
require_type_urls.emplace(factory_type_url);
}

ProtobufTypes::MessagePtr default_config;
if (config_source.has_default_config()) {
default_config =
getDefaultConfig(config_source.default_config(), filter_config_name, factory_context,
last_filter_in_filter_chain, filter_chain_type, require_type_urls);
}

auto provider = std::make_unique<DynamicFilterConfigProviderImpl>(
subscription, require_type_urls, factory_context, std::move(default_config),
last_filter_in_filter_chain, filter_chain_type,
[this, stat_prefix,
&factory_context](const Protobuf::Message& message) -> Envoy::Http::FilterFactoryCb {
return instantiateFilterFactory(message, stat_prefix, factory_context);
});

// Ensure the subscription starts if it has not already.
if (config_source.apply_default_config_without_warming()) {
factory_context.initManager().add(provider->initTarget());
}
applyLastOrDefaultConfig(subscription, *provider, filter_config_name);
return provider;
std::tuple<ProtobufTypes::MessagePtr, std::string, bool>
HttpFilterConfigProviderManagerImpl::getMessage(
const envoy::config::core::v3::TypedExtensionConfig& filter_config,
Server::Configuration::FactoryContext& factory_context) const {
auto& factory =
Config::Utility::getAndCheckFactory<Server::Configuration::NamedHttpFilterConfigFactory>(
filter_config);
ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
filter_config.typed_config(),
factory_context.messageValidationContext().dynamicValidationVisitor(), factory);
bool is_terminal_filter = factory.isTerminalFilterByProto(*message, factory_context);
return {std::move(message), factory.name(), is_terminal_filter};
}

ProtobufTypes::MessagePtr HttpFilterConfigProviderManagerImpl::getDefaultConfig(
Expand Down
105 changes: 77 additions & 28 deletions source/common/filter/config_discovery_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,17 @@ class DynamicFilterConfigProviderImplBase : public Config::DynamicExtensionConfi
/**
* Implementation of a filter config provider using discovery subscriptions.
**/
template <class FactoryCb>
class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBase,
public DynamicFilterConfigProvider {
public DynamicFilterConfigProvider<FactoryCb> {
public:
DynamicFilterConfigProviderImpl(
FilterConfigSubscriptionSharedPtr& subscription,
const absl::flat_hash_set<std::string>& require_type_urls,
Server::Configuration::FactoryContext& factory_context,
ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain,
const std::string& filter_chain_type,
std::function<Envoy::Http::FilterFactoryCb(const Protobuf::Message&)> factory_cb_fn)
DynamicFilterConfigProviderImpl(FilterConfigSubscriptionSharedPtr& subscription,
const absl::flat_hash_set<std::string>& require_type_urls,
Server::Configuration::FactoryContext& factory_context,
ProtobufTypes::MessagePtr&& default_config,
bool last_filter_in_filter_chain,
const std::string& filter_chain_type,
std::function<FactoryCb(const Protobuf::Message&)> factory_cb_fn)
: DynamicFilterConfigProviderImplBase(subscription, require_type_urls,
last_filter_in_filter_chain, filter_chain_type),
default_configuration_(std::move(default_config)), tls_(factory_context.threadLocal()),
Expand All @@ -81,12 +82,12 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa

// Config::ExtensionConfigProvider
const std::string& name() override { return DynamicFilterConfigProviderImplBase::name(); }
absl::optional<Envoy::Http::FilterFactoryCb> config() override { return tls_->config_; }
absl::optional<FactoryCb> config() override { return tls_->config_; }

// Config::DynamicExtensionConfigProviderBase
void onConfigUpdate(const Protobuf::Message& message, const std::string&,
Config::ConfigAppliedCb cb) override {
const Envoy::Http::FilterFactoryCb config = factory_cb_fn_(message);
const FactoryCb config = factory_cb_fn_(message);
tls_.runOnAllThreads(
[config, cb](OptRef<ThreadLocalConfig> tls) {
tls->config_ = config;
Expand All @@ -102,7 +103,7 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa
}

void onConfigRemoved(Config::ConfigAppliedCb applied_on_all_threads) override {
const absl::optional<Envoy::Http::FilterFactoryCb> default_config =
const absl::optional<FactoryCb> default_config =
default_configuration_ ? absl::make_optional(factory_cb_fn_(*default_configuration_))
: absl::nullopt;
tls_.runOnAllThreads(
Expand All @@ -126,15 +127,15 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa
private:
struct ThreadLocalConfig : public ThreadLocal::ThreadLocalObject {
ThreadLocalConfig() : config_{absl::nullopt} {}
absl::optional<Envoy::Http::FilterFactoryCb> config_{};
absl::optional<FactoryCb> config_{};
};

// Currently applied configuration to ensure that the main thread deletes the last reference to
// it.
absl::optional<Envoy::Http::FilterFactoryCb> current_config_{absl::nullopt};
absl::optional<FactoryCb> current_config_{absl::nullopt};
const ProtobufTypes::MessagePtr default_configuration_;
ThreadLocal::TypedSlot<ThreadLocalConfig> tls_;
const std::function<Envoy::Http::FilterFactoryCb(const Protobuf::Message&)> factory_cb_fn_;
const std::function<FactoryCb(const Protobuf::Message&)> factory_cb_fn_;
};

/**
Expand Down Expand Up @@ -199,7 +200,6 @@ class FilterConfigSubscription
std::string last_filter_name_;
bool last_filter_is_terminal_;
Server::Configuration::FactoryContext& factory_context_;
ProtobufMessage::ValidationVisitor& validator_;

Init::SharedTargetImpl init_target_;
bool started_{false};
Expand All @@ -222,25 +222,32 @@ class FilterConfigSubscription
/**
* Provider implementation of a static filter config.
**/
class StaticFilterConfigProviderImpl : public FilterConfigProvider {
template <class FactoryCb>
class StaticFilterConfigProviderImpl : public FilterConfigProvider<FactoryCb> {
public:
StaticFilterConfigProviderImpl(const Envoy::Http::FilterFactoryCb& config,
const std::string filter_config_name)
StaticFilterConfigProviderImpl(const FactoryCb& config, const std::string filter_config_name)
: config_(config), filter_config_name_(filter_config_name) {}

// Config::ExtensionConfigProvider
const std::string& name() override { return filter_config_name_; }
absl::optional<Envoy::Http::FilterFactoryCb> config() override { return config_; }
absl::optional<FactoryCb> config() override { return config_; }

private:
Envoy::Http::FilterFactoryCb config_;
FactoryCb config_;
const std::string filter_config_name_;
};

/**
* Base class for a FilterConfigProviderManager.
*/
class FilterConfigProviderManagerImplBase : Logger::Loggable<Logger::Id::filter> {
public:
virtual ~FilterConfigProviderManagerImplBase() = default;

virtual std::tuple<ProtobufTypes::MessagePtr, std::string, bool>
getMessage(const envoy::config::core::v3::TypedExtensionConfig& filter_config,
Server::Configuration::FactoryContext& factory_context) const PURE;

protected:
std::shared_ptr<FilterConfigSubscription>
getSubscription(const envoy::config::core::v3::ConfigSource& config_source,
Expand All @@ -258,20 +265,56 @@ class FilterConfigProviderManagerImplBase : Logger::Loggable<Logger::Id::filter>
/**
* An implementation of FilterConfigProviderManager.
*/
template <class FactoryCb>
class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBase,
public FilterConfigProviderManager,
public FilterConfigProviderManager<FactoryCb>,
public Singleton::Instance {
public:
DynamicFilterConfigProviderPtr createDynamicFilterConfigProvider(
DynamicFilterConfigProviderPtr<FactoryCb> createDynamicFilterConfigProvider(
const envoy::config::core::v3::ExtensionConfigSource& config_source,
const std::string& filter_config_name, Server::Configuration::FactoryContext& factory_context,
const std::string& stat_prefix, bool last_filter_in_filter_chain,
const std::string& filter_chain_type) override;
const std::string& filter_chain_type) override {
auto subscription = getSubscription(config_source.config_source(), filter_config_name,
factory_context, stat_prefix);
// For warming, wait until the subscription receives the first response to indicate readiness.
// Otherwise, mark ready immediately and start the subscription on initialization. A default
// config is expected in the latter case.
if (!config_source.apply_default_config_without_warming()) {
factory_context.initManager().add(subscription->initTarget());
}
absl::flat_hash_set<std::string> require_type_urls;
for (const auto& type_url : config_source.type_urls()) {
auto factory_type_url = TypeUtil::typeUrlToDescriptorFullName(type_url);
require_type_urls.emplace(factory_type_url);
}

ProtobufTypes::MessagePtr default_config;
if (config_source.has_default_config()) {
default_config =
getDefaultConfig(config_source.default_config(), filter_config_name, factory_context,
last_filter_in_filter_chain, filter_chain_type, require_type_urls);
}

FilterConfigProviderPtr
createStaticFilterConfigProvider(const Envoy::Http::FilterFactoryCb& config,
auto provider = std::make_unique<DynamicFilterConfigProviderImpl<FactoryCb>>(
subscription, require_type_urls, factory_context, std::move(default_config),
last_filter_in_filter_chain, filter_chain_type,
[this, stat_prefix, &factory_context](const Protobuf::Message& message) -> FactoryCb {
return instantiateFilterFactory(message, stat_prefix, factory_context);
});

// Ensure the subscription starts if it has not already.
if (config_source.apply_default_config_without_warming()) {
factory_context.initManager().add(provider->initTarget());
}
applyLastOrDefaultConfig(subscription, *provider, filter_config_name);
return provider;
}

FilterConfigProviderPtr<FactoryCb>
createStaticFilterConfigProvider(const FactoryCb& config,
const std::string& filter_config_name) override {
return std::make_unique<StaticFilterConfigProviderImpl>(config, filter_config_name);
return std::make_unique<StaticFilterConfigProviderImpl<FactoryCb>>(config, filter_config_name);
}

protected:
Expand All @@ -281,12 +324,18 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa
bool last_filter_in_filter_chain, const std::string& filter_chain_type,
const absl::flat_hash_set<std::string>& require_type_urls) const PURE;

virtual Http::FilterFactoryCb
virtual FactoryCb
instantiateFilterFactory(const Protobuf::Message& message, const std::string& stat_prefix,
Server::Configuration::FactoryContext& factory_context) const PURE;
};

class HttpFilterConfigProviderManagerImpl : public FilterConfigProviderManagerImpl {
class HttpFilterConfigProviderManagerImpl
: public FilterConfigProviderManagerImpl<Http::FilterFactoryCb> {
public:
std::tuple<ProtobufTypes::MessagePtr, std::string, bool>
getMessage(const envoy::config::core::v3::TypedExtensionConfig& filter_config,
Server::Configuration::FactoryContext& factory_context) const override;

protected:
ProtobufTypes::MessagePtr
getDefaultConfig(const ProtobufWkt::Any& proto_config, const std::string& filter_config_name,
Expand Down
Loading

0 comments on commit 0264041

Please sign in to comment.