Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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