Skip to content

Commit

Permalink
add ecds support for composite filter (#29289)
Browse files Browse the repository at this point in the history
Commit Message: add ecds support for composite filter
Additional Description: Currrently ECDS does not support composite filter. This would help to use composite filter for use cases like WASM filters
Risk Level: Low
Testing: Updated
Docs Changes: Updated
Release Notes: Added

Signed-off-by: Rama Chavali <rama.rao@salesforce.com>
  • Loading branch information
ramaraochavali authored Dec 14, 2023
1 parent a9c1219 commit b4fba1a
Show file tree
Hide file tree
Showing 22 changed files with 349 additions and 70 deletions.
27 changes: 26 additions & 1 deletion api/envoy/extensions/filters/http/composite/v3/composite.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ syntax = "proto3";

package envoy.extensions.filters.http.composite.v3;

import "envoy/config/core/v3/config_source.proto";
import "envoy/config/core/v3/extension.proto";

import "udpa/annotations/migrate.proto";
import "udpa/annotations/status.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.filters.http.composite.v3";
option java_outer_classname = "CompositeProto";
Expand All @@ -29,8 +32,30 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
message Composite {
}

// Configuration for an extension configuration discovery service with name.
message DynamicConfig {
// The name of the extension configuration. It also serves as a resource name in ExtensionConfigDS.
string name = 1 [(validate.rules).string = {min_len: 1}];

// Configuration source specifier for an extension configuration discovery
// service. In case of a failure and without the default configuration,
// 500(Internal Server Error) will be returned.
config.core.v3.ExtensionConfigSource config_discovery = 2;
}

// Composite match action (see :ref:`matching docs <arch_overview_matching_api>` for more info on match actions).
// This specifies the filter configuration of the filter that the composite filter should delegate filter interactions to.
message ExecuteFilterAction {
config.core.v3.TypedExtensionConfig typed_config = 1;
// Filter specific configuration which depends on the filter being
// instantiated. See the supported filters for further documentation.
// Only one of ``typed_config`` or ``dynamic_config`` can be set.
// [#extension-category: envoy.filters.http]
config.core.v3.TypedExtensionConfig typed_config = 1
[(udpa.annotations.field_migrate).oneof_promotion = "config_type"];

// Dynamic configuration of filter obtained via extension configuration discovery
// service.
// Only one of ``typed_config`` or ``dynamic_config`` can be set.
DynamicConfig dynamic_config = 2
[(udpa.annotations.field_migrate).oneof_promotion = "config_type"];
}
4 changes: 4 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ removed_config_or_runtime:
runtime flag and legacy code path.
new_features:
- area: composite filter
change: |
added :ref:`ExtensionConfiguration discovery service<envoy_v3_api_file_envoy/service/extension/v3/config_discovery.proto>` support for
:ref:`composite filter <config_http_filters_composite>`.
- area: aws
change: |
Added support for AWS common utility to fetch metadata credentials from AWS STS by using ``WebIdentityToken``. To enable
Expand Down
8 changes: 8 additions & 0 deletions envoy/http/filter_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ class FilterChainFactoryCallbacks;
*/
using FilterFactoryCb = std::function<void(FilterChainFactoryCallbacks& callbacks)>;

// Struct of canonical filter name and HTTP stream filter factory callback.
struct NamedHttpFilterFactoryCb {
// Canonical filter name.
std::string name;
// Factory function used to create filter instances.
Http::FilterFactoryCb factory_cb;
};

/**
* Simple struct of additional contextual information of HTTP filter, e.g. filter config name
* from configuration, canonical filter name, etc.
Expand Down
1 change: 1 addition & 0 deletions envoy/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ envoy_cc_library(
":process_context_interface",
"//envoy/access_log:access_log_interface",
"//envoy/api:api_interface",
"//envoy/config:dynamic_extension_config_provider_interface",
"//envoy/config:typed_config_interface",
"//envoy/config:typed_metadata_interface",
"//envoy/grpc:context_interface",
Expand Down
22 changes: 22 additions & 0 deletions envoy/server/factory_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "envoy/access_log/access_log.h"
#include "envoy/common/random_generator.h"
#include "envoy/config/core/v3/base.pb.h"
#include "envoy/config/dynamic_extension_config_provider.h"
#include "envoy/config/typed_config.h"
#include "envoy/config/typed_metadata.h"
#include "envoy/grpc/context.h"
Expand Down Expand Up @@ -36,9 +37,14 @@
#include "source/common/protobuf/protobuf.h"

namespace Envoy {
namespace Filter {
template <class FactoryCb, class FactoryCtx> class FilterConfigProviderManager;
} // namespace Filter
namespace Server {
namespace Configuration {

using HttpExtensionConfigProviderSharedPtr =
std::shared_ptr<Config::DynamicExtensionConfigProvider<Envoy::Http::NamedHttpFilterFactoryCb>>;
/**
* Common interface for downstream and upstream network filters to access server
* wide resources. This could be treated as limited form of server factory context.
Expand Down Expand Up @@ -131,6 +137,14 @@ class CommonFactoryContext {
virtual ServerLifecycleNotifier& lifecycleNotifier() PURE;
};

class FactoryContext;

using DownstreamHTTPFilterConfigProviderManager =
Filter::FilterConfigProviderManager<Http::NamedHttpFilterFactoryCb,
Server::Configuration::FactoryContext>;
using DownstreamHTTPFilterConfigProviderManagerSharedPtr =
std::shared_ptr<DownstreamHTTPFilterConfigProviderManager>;

/**
* ServerFactoryContext is an specialization of common interface for downstream and upstream network
* filters. The implementation guarantees the lifetime is no shorter than server. It could be used
Expand Down Expand Up @@ -195,6 +209,14 @@ class ServerFactoryContext : public virtual CommonFactoryContext {
* @return whether external healthchecks are currently failed or not.
*/
virtual bool healthCheckFailed() const PURE;

/**
* Returns the downstream HTTP filter config provider manager.
*
* @return DownstreamHTTPFilterConfigProviderManagerSharedPtr
*/
virtual DownstreamHTTPFilterConfigProviderManagerSharedPtr
downstreamHttpFilterConfigProviderManager() PURE;
};

/**
Expand Down
19 changes: 6 additions & 13 deletions source/common/filter/config_discovery_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa
if (!tls->isShutdown()) {
tls->runOnAllThreads([](OptRef<ThreadLocalConfig> tls) { tls->config_ = {}; },
// Extend the lifetime of TLS by capturing main_config_, because
// otherwise, the callback to clear TLS worker content is not executed.
// otherwise, the callback to clear TLS worker content is not
// executed.
[main_config = main_config_]() {
// Explicitly delete TLS on the main thread.
main_config->tls_.reset();
Expand Down Expand Up @@ -178,20 +179,12 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa
const ProtobufTypes::MessagePtr default_configuration_;
};

// Struct of canonical filter name and HTTP stream filter factory callback.
struct NamedHttpFilterFactoryCb {
// Canonical filter name.
std::string name;
// Factory function used to create filter instances.
Http::FilterFactoryCb factory_cb;
};

// Implementation of a HTTP dynamic filter config provider.
// NeutralHttpFilterConfigFactory can either be a NamedHttpFilterConfigFactory
// or an UpstreamHttpFilterConfigFactory.
template <class FactoryCtx, class NeutralHttpFilterConfigFactory>
class HttpDynamicFilterConfigProviderImpl
: public DynamicFilterConfigProviderImpl<NamedHttpFilterFactoryCb> {
: public DynamicFilterConfigProviderImpl<Http::NamedHttpFilterFactoryCb> {
public:
HttpDynamicFilterConfigProviderImpl(
FilterConfigSubscriptionSharedPtr& subscription,
Expand All @@ -215,7 +208,7 @@ class HttpDynamicFilterConfigProviderImpl
}

private:
NamedHttpFilterFactoryCb
Http::NamedHttpFilterFactoryCb
instantiateFilterFactory(const Protobuf::Message& message) const override {
auto* factory = Registry::FactoryRegistry<NeutralHttpFilterConfigFactory>::getFactoryByType(
message.GetTypeName());
Expand Down Expand Up @@ -648,7 +641,7 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa
// HTTP filter
class HttpFilterConfigProviderManagerImpl
: public FilterConfigProviderManagerImpl<
Server::Configuration::NamedHttpFilterConfigFactory, NamedHttpFilterFactoryCb,
Server::Configuration::NamedHttpFilterConfigFactory, Http::NamedHttpFilterFactoryCb,
Server::Configuration::FactoryContext,
HttpDynamicFilterConfigProviderImpl<
Server::Configuration::FactoryContext,
Expand All @@ -675,7 +668,7 @@ class HttpFilterConfigProviderManagerImpl
// HTTP filter
class UpstreamHttpFilterConfigProviderManagerImpl
: public FilterConfigProviderManagerImpl<
Server::Configuration::UpstreamHttpFilterConfigFactory, NamedHttpFilterFactoryCb,
Server::Configuration::UpstreamHttpFilterConfigFactory, Http::NamedHttpFilterFactoryCb,
Server::Configuration::UpstreamFactoryContext,
HttpDynamicFilterConfigProviderImpl<
Server::Configuration::UpstreamFactoryContext,
Expand Down
32 changes: 1 addition & 31 deletions source/common/http/filter_chain_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,14 @@

#include "envoy/registry/registry.h"

#include "source/common/common/empty_string.h"
#include "source/common/common/fmt.h"
#include "source/common/config/utility.h"
#include "source/common/http/utility.h"
#include "source/common/protobuf/utility.h"
#include "source/extensions/filters/http/common/pass_through_filter.h"

namespace Envoy {
namespace Http {

// Allows graceful handling of missing configuration for ECDS.
class MissingConfigFilter : public Http::PassThroughDecoderFilter {
public:
Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool) override {
decoder_callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::NoFilterConfigFound);
decoder_callbacks_->sendLocalReply(Http::Code::InternalServerError, EMPTY_STRING, nullptr,
absl::nullopt, EMPTY_STRING);
return Http::FilterHeadersStatus::StopIteration;
}
};

static Http::FilterFactoryCb MissingConfigFilterFactory =
[](Http::FilterChainFactoryCallbacks& cb) {
cb.addStreamDecoderFilter(std::make_shared<MissingConfigFilter>());
};

void FilterChainUtility::createFilterChainForFactories(
Http::FilterChainManager& manager, const FilterChainOptions& options,
const FilterFactoriesList& filter_factories) {
Expand All @@ -44,7 +26,7 @@ void FilterChainUtility::createFilterChainForFactories(

auto config = filter_config_provider.provider->config();
if (config.has_value()) {
Filter::NamedHttpFilterFactoryCb& factory_cb = config.value().get();
Http::NamedHttpFilterFactoryCb& factory_cb = config.value().get();
manager.applyFilterFactoryCb({filter_config_provider.provider->name(), factory_cb.name},
factory_cb.factory_cb);
continue;
Expand All @@ -63,7 +45,6 @@ void FilterChainUtility::createFilterChainForFactories(
}
}

SINGLETON_MANAGER_REGISTRATION(downstream_filter_config_provider_manager);
SINGLETON_MANAGER_REGISTRATION(upstream_filter_config_provider_manager);

std::shared_ptr<UpstreamFilterConfigProviderManager>
Expand All @@ -76,16 +57,5 @@ FilterChainUtility::createSingletonUpstreamFilterConfigProviderManager(
return upstream_filter_config_provider_manager;
}

std::shared_ptr<Http::DownstreamFilterConfigProviderManager>
FilterChainUtility::createSingletonDownstreamFilterConfigProviderManager(
Server::Configuration::ServerFactoryContext& context) {
std::shared_ptr<Http::DownstreamFilterConfigProviderManager>
downstream_filter_config_provider_manager =
context.singletonManager().getTyped<Http::DownstreamFilterConfigProviderManager>(
SINGLETON_MANAGER_REGISTERED_NAME(downstream_filter_config_provider_manager),
[] { return std::make_shared<Filter::HttpFilterConfigProviderManagerImpl>(); });
return downstream_filter_config_provider_manager;
}

} // namespace Http
} // namespace Envoy
31 changes: 21 additions & 10 deletions source/common/http/filter_chain_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,39 @@
#include "envoy/filter/config_provider_manager.h"
#include "envoy/http/filter.h"

#include "source/common/common/empty_string.h"
#include "source/common/common/logger.h"
#include "source/common/filter/config_discovery_impl.h"
#include "source/common/http/dependency_manager.h"
#include "source/extensions/filters/http/common/pass_through_filter.h"

namespace Envoy {
namespace Http {

using DownstreamFilterConfigProviderManager =
Filter::FilterConfigProviderManager<Filter::NamedHttpFilterFactoryCb,
Server::Configuration::FactoryContext>;
using UpstreamFilterConfigProviderManager =
Filter::FilterConfigProviderManager<Filter::NamedHttpFilterFactoryCb,
Filter::FilterConfigProviderManager<Http::NamedHttpFilterFactoryCb,
Server::Configuration::UpstreamFactoryContext>;

// Allows graceful handling of missing configuration for ECDS.
class MissingConfigFilter : public Http::PassThroughDecoderFilter {
public:
Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool) override {
decoder_callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::NoFilterConfigFound);
decoder_callbacks_->sendLocalReply(Http::Code::InternalServerError, EMPTY_STRING, nullptr,
absl::nullopt, EMPTY_STRING);
return Http::FilterHeadersStatus::StopIteration;
}
};

static Http::FilterFactoryCb MissingConfigFilterFactory =
[](Http::FilterChainFactoryCallbacks& cb) {
cb.addStreamDecoderFilter(std::make_shared<MissingConfigFilter>());
};

class FilterChainUtility : Logger::Loggable<Logger::Id::config> {
public:
struct FilterFactoryProvider {
Filter::FilterConfigProviderPtr<Filter::NamedHttpFilterFactoryCb> provider;
Filter::FilterConfigProviderPtr<Http::NamedHttpFilterFactoryCb> provider;
// If true, this filter is disabled by default and must be explicitly enabled by
// route configuration.
bool disabled{};
Expand All @@ -37,10 +52,6 @@ class FilterChainUtility : Logger::Loggable<Logger::Id::config> {
const FilterChainOptions& options,
const FilterFactoriesList& filter_factories);

static std::shared_ptr<DownstreamFilterConfigProviderManager>
createSingletonDownstreamFilterConfigProviderManager(
Server::Configuration::ServerFactoryContext& context);

static std::shared_ptr<UpstreamFilterConfigProviderManager>
createSingletonUpstreamFilterConfigProviderManager(
Server::Configuration::ServerFactoryContext& context);
Expand All @@ -51,7 +62,7 @@ class FilterChainHelper : Logger::Loggable<Logger::Id::config> {
public:
using FilterFactoriesList = FilterChainUtility::FilterFactoriesList;
using FilterConfigProviderManager =
Filter::FilterConfigProviderManager<Filter::NamedHttpFilterFactoryCb, FilterCtx>;
Filter::FilterConfigProviderManager<Http::NamedHttpFilterFactoryCb, FilterCtx>;

FilterChainHelper(FilterConfigProviderManager& filter_config_provider_manager,
Server::Configuration::ServerFactoryContext& server_context,
Expand Down
1 change: 1 addition & 0 deletions source/extensions/filters/http/composite/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ envoy_cc_library(
srcs = ["action.cc"],
hdrs = ["action.h"],
deps = [
"//source/common/http:filter_chain_helper_lib",
"//source/common/http/matching:data_impl_lib",
"//source/common/matcher:matcher_lib",
"@envoy_api//envoy/extensions/filters/http/composite/v3:pkg_cc_proto",
Expand Down
31 changes: 31 additions & 0 deletions source/extensions/filters/http/composite/action.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,37 @@ Matcher::ActionFactoryCb ExecuteFilterActionFactory::createActionFactoryCb(
const envoy::extensions::filters::http::composite::v3::ExecuteFilterAction&>(
config, validation_visitor);

if (composite_action.has_dynamic_config() && composite_action.has_typed_config()) {
throw EnvoyException(
fmt::format("Error: Only one of `dynamic_config` or `typed_config` can be set."));
}

if (composite_action.has_dynamic_config()) {
if (!context.factory_context_.has_value() || !context.server_factory_context_.has_value()) {
throw EnvoyException(fmt::format("Failed to get factory context or server factory context."));
}
// Create a dynamic filter config provider and register it with the server factory context.
auto config_discovery = composite_action.dynamic_config().config_discovery();
Server::Configuration::FactoryContext& factory_context = context.factory_context_.value();
Server::Configuration::ServerFactoryContext& server_factory_context =
context.server_factory_context_.value();
Server::Configuration::HttpExtensionConfigProviderSharedPtr provider =
server_factory_context.downstreamHttpFilterConfigProviderManager()
->createDynamicFilterConfigProvider(
config_discovery, composite_action.dynamic_config().name(), server_factory_context,
factory_context, server_factory_context.clusterManager(), false, "http", nullptr);
return [provider = std::move(provider)]() -> Matcher::ActionPtr {
auto config_value = provider->config();
if (config_value.has_value()) {
auto factory_cb = config_value.value().get().factory_cb;
return std::make_unique<ExecuteFilterAction>(factory_cb);
}
// There is no dynamic config available. Apply missing config filter.
auto factory_cb = Envoy::Http::MissingConfigFilterFactory;
return std::make_unique<ExecuteFilterAction>(factory_cb);
};
}

auto& factory =
Config::Utility::getAndCheckFactory<Server::Configuration::NamedHttpFilterConfigFactory>(
composite_action.typed_config());
Expand Down
1 change: 1 addition & 0 deletions source/extensions/filters/http/composite/action.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "envoy/extensions/filters/http/composite/v3/composite.pb.validate.h"

#include "source/common/http/filter_chain_helper.h"
#include "source/common/http/matching/data_impl.h"
#include "source/common/matcher/matcher.h"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,9 @@ Utility::Singletons Utility::createSingletons(Server::Configuration::FactoryCont

auto tracer_manager = Tracing::TracerManagerImpl::singleton(context);

std::shared_ptr<Http::DownstreamFilterConfigProviderManager> filter_config_provider_manager =
Http::FilterChainUtility::createSingletonDownstreamFilterConfigProviderManager(
server_context);
Server::Configuration::DownstreamHTTPFilterConfigProviderManagerSharedPtr
filter_config_provider_manager =
context.serverFactoryContext().downstreamHttpFilterConfigProviderManager();

return {date_provider, route_config_provider_manager, scoped_routes_config_provider_manager,
tracer_manager, filter_config_provider_manager};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace NetworkFilters {
namespace HttpConnectionManager {

using FilterConfigProviderManager =
Filter::FilterConfigProviderManager<Filter::NamedHttpFilterFactoryCb,
Filter::FilterConfigProviderManager<Http::NamedHttpFilterFactoryCb,
Server::Configuration::FactoryContext>;

/**
Expand Down
Loading

0 comments on commit b4fba1a

Please sign in to comment.