Skip to content

Commit

Permalink
alpha matching: support generic action factory context (#17025)
Browse files Browse the repository at this point in the history
Prior to this PR the MatchTreeFactory requried a ServerFactoryContext in
order to pass this to the action factories. This is not available in all
contexts, and there are other possible use cases where we might need
to pass additional information to the match tree factory (e.g. the
router might need to pass the parent vhost configuration).

This PR introduces a paramterized action context, allowing each usage of
the MatchTree to define its own data that should be presented to the
action factories. This also has the nice benefit of partitioning the
action factories per context: each unique ActionFactoryContext defines a
new factory type, ensuring that actions defined for a HTTP filter
context won't be conflated with actions defined for another context.

Signed-off-by: Snow Pettersen <[email protected]>
  • Loading branch information
snowp authored Jun 29, 2021
1 parent 3d9c533 commit be1cd7b
Show file tree
Hide file tree
Showing 19 changed files with 97 additions and 90 deletions.
13 changes: 7 additions & 6 deletions envoy/matcher/matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ class Action {
using ActionPtr = std::unique_ptr<Action>;
using ActionFactoryCb = std::function<ActionPtr()>;

class ActionFactory : public Config::TypedFactory {
template <class ActionFactoryContext> class ActionFactory : public Config::TypedFactory {
public:
virtual ActionFactoryCb
createActionFactoryCb(const Protobuf::Message& config, const std::string& stats_prefix,
Server::Configuration::FactoryContext& context) PURE;
createActionFactoryCb(const Protobuf::Message& config,
ActionFactoryContext& action_factory_context,
ProtobufMessage::ValidationVisitor& validation_visitor) PURE;

std::string category() const override { return "envoy.matching.action"; }
};
Expand Down Expand Up @@ -155,7 +156,7 @@ class InputMatcherFactory : public Config::TypedFactory {
public:
virtual InputMatcherFactoryCb
createInputMatcherFactoryCb(const Protobuf::Message& config,
Server::Configuration::FactoryContext& factory_context) PURE;
ProtobufMessage::ValidationVisitor& validation_visitor) PURE;

std::string category() const override { return "envoy.matching.input_matchers"; }
};
Expand Down Expand Up @@ -226,7 +227,7 @@ template <class DataType> class DataInputFactory : public Config::TypedFactory {
*/
virtual DataInputFactoryCb<DataType>
createDataInputFactoryCb(const Protobuf::Message& config,
Server::Configuration::FactoryContext& factory_context) PURE;
ProtobufMessage::ValidationVisitor& validation_visitor) PURE;

/**
* The category of this factory depends on the DataType, so we require a name() function to exist
Expand Down Expand Up @@ -262,7 +263,7 @@ class CommonProtocolInputFactory : public Config::TypedFactory {
*/
virtual CommonProtocolInputFactoryCb
createCommonProtocolInputFactoryCb(const Protobuf::Message& config,
Server::Configuration::FactoryContext& factory_context) PURE;
ProtobufMessage::ValidationVisitor& validation_visitor) PURE;

std::string category() const override { return "envoy.matching.common_inputs"; }
};
Expand Down
4 changes: 3 additions & 1 deletion source/common/http/filter_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
#include "source/common/http/header_utility.h"
#include "source/common/http/utility.h"

#include "matching/data_impl.h"

namespace Envoy {
namespace Http {

namespace {
REGISTER_FACTORY(SkipActionFactory, Matcher::ActionFactory);
REGISTER_FACTORY(SkipActionFactory, Matcher::ActionFactory<Matching::HttpFilterActionContext>);

template <class T> using FilterList = std::list<std::unique_ptr<T>>;

Expand Down
7 changes: 4 additions & 3 deletions source/common/http/filter_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,12 @@ class FilterMatchState {

using FilterMatchStateSharedPtr = std::shared_ptr<FilterMatchState>;

class SkipActionFactory : public Matcher::ActionFactory {
class SkipActionFactory : public Matcher::ActionFactory<Matching::HttpFilterActionContext> {
public:
std::string name() const override { return "skip"; }
Matcher::ActionFactoryCb createActionFactoryCb(const Protobuf::Message&, const std::string&,
Server::Configuration::FactoryContext&) override {
Matcher::ActionFactoryCb createActionFactoryCb(const Protobuf::Message&,
Matching::HttpFilterActionContext&,
ProtobufMessage::ValidationVisitor&) override {
return []() { return std::make_unique<SkipAction>(); };
}
ProtobufTypes::MessagePtr createEmptyConfigProto() override {
Expand Down
1 change: 1 addition & 0 deletions source/common/http/match_wrapper/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ envoy_cc_library(
deps = [
"//envoy/registry",
"//envoy/server:filter_config_interface",
"//source/common/http/matching:data_impl_lib",
"//source/common/matcher:matcher_lib",
"//source/extensions/filters/http/common:factory_base_lib",
"@envoy_api//envoy/extensions/common/matching/v3:pkg_cc_proto",
Expand Down
9 changes: 6 additions & 3 deletions source/common/http/match_wrapper/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "envoy/registry/registry.h"

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

#include "absl/status/status.h"
Expand Down Expand Up @@ -103,9 +104,11 @@ Envoy::Http::FilterFactoryCb MatchWrapperConfig::createFilterFactoryFromProtoTyp

MatchTreeValidationVisitor validation_visitor(*factory.matchingRequirements());

auto match_tree =
Matcher::MatchTreeFactory<Envoy::Http::HttpMatchingData>(prefix, context, validation_visitor)
.create(proto_config.matcher());
Envoy::Http::Matching::HttpFilterActionContext action_context{prefix, context};
auto match_tree = Matcher::MatchTreeFactory<Envoy::Http::HttpMatchingData,
Envoy::Http::Matching::HttpFilterActionContext>(
action_context, context.messageValidationVisitor(), validation_visitor)
.create(proto_config.matcher());

if (!validation_visitor.errors().empty()) {
// TODO(snowp): Output all violations.
Expand Down
4 changes: 4 additions & 0 deletions source/common/http/matching/data_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ class HttpMatchingDataImpl : public HttpMatchingData {

using HttpMatchingDataImplSharedPtr = std::shared_ptr<HttpMatchingDataImpl>;

struct HttpFilterActionContext {
const std::string& stat_prefix_;
Server::Configuration::FactoryContext& factory_context_;
};
} // namespace Matching
} // namespace Http
} // namespace Envoy
6 changes: 3 additions & 3 deletions source/common/http/matching/inputs.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ class HttpHeadersDataInputFactoryBase : public Matcher::DataInputFactory<HttpMat

Matcher::DataInputFactoryCb<HttpMatchingData>
createDataInputFactoryCb(const Protobuf::Message& config,
Server::Configuration::FactoryContext& factory_context) override {
const auto& typed_config = MessageUtil::downcastAndValidate<const ProtoType&>(
config, factory_context.messageValidationVisitor());
ProtobufMessage::ValidationVisitor& validation_visitor) override {
const auto& typed_config =
MessageUtil::downcastAndValidate<const ProtoType&>(config, validation_visitor);

return [header_name = typed_config.header_name()] {
return std::make_unique<DataInputType>(header_name);
Expand Down
37 changes: 20 additions & 17 deletions source/common/matcher/matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@ template <class DataType> using DataInputFactoryCb = std::function<DataInputPtr<

/**
* Recursively constructs a MatchTree from a protobuf configuration.
* @param DataType the type used as a source for DataInputs
* @param ActionFactoryContext the context provided to Action factories
*/
template <class DataType> class MatchTreeFactory {
template <class DataType, class ActionFactoryContext> class MatchTreeFactory {
public:
MatchTreeFactory(const std::string& stats_prefix,
Server::Configuration::FactoryContext& factory_context,
MatchTreeFactory(ActionFactoryContext& context,
ProtobufMessage::ValidationVisitor& proto_validation_visitor,
MatchTreeValidationVisitor<DataType>& validation_visitor)
: stats_prefix_(stats_prefix), factory_context_(factory_context),
: action_factory_context_(context), proto_validation_visitor_(proto_validation_visitor),
validation_visitor_(validation_visitor) {}

MatchTreeFactoryCb<DataType> create(const envoy::config::common::matcher::v3::Matcher& config) {
Expand Down Expand Up @@ -200,12 +202,13 @@ template <class DataType> class MatchTreeFactory {
return OnMatch<DataType>{{}, matcher_factory()};
};
} else if (on_match.has_action()) {
auto& factory = Config::Utility::getAndCheckFactory<ActionFactory>(on_match.action());
auto& factory = Config::Utility::getAndCheckFactory<ActionFactory<ActionFactoryContext>>(
on_match.action());
ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
on_match.action().typed_config(), factory_context_.messageValidationVisitor(), factory);
on_match.action().typed_config(), proto_validation_visitor_, factory);

auto action_factory =
factory.createActionFactoryCb(*message, stats_prefix_, factory_context_);
auto action_factory = factory.createActionFactoryCb(*message, action_factory_context_,
proto_validation_visitor_);
return [action_factory] { return OnMatch<DataType>{action_factory, {}}; };
}

Expand Down Expand Up @@ -234,8 +237,8 @@ template <class DataType> class MatchTreeFactory {
validation_visitor_.validateDataInput(*factory, config.typed_config().type_url());

ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
config.typed_config(), factory_context_.messageValidationVisitor(), *factory);
auto data_input = factory->createDataInputFactoryCb(*message, factory_context_);
config.typed_config(), proto_validation_visitor_, *factory);
auto data_input = factory->createDataInputFactoryCb(*message, proto_validation_visitor_);
return data_input;
}

Expand All @@ -244,9 +247,9 @@ template <class DataType> class MatchTreeFactory {
auto& common_input_factory =
Config::Utility::getAndCheckFactory<CommonProtocolInputFactory>(config);
ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
config.typed_config(), factory_context_.messageValidationVisitor(), common_input_factory);
auto common_input =
common_input_factory.createCommonProtocolInputFactoryCb(*message, factory_context_);
config.typed_config(), proto_validation_visitor_, common_input_factory);
auto common_input = common_input_factory.createCommonProtocolInputFactoryCb(
*message, proto_validation_visitor_);
return
[common_input]() { return std::make_unique<CommonProtocolInputWrapper>(common_input()); };
}
Expand All @@ -265,17 +268,17 @@ template <class DataType> class MatchTreeFactory {
auto& factory =
Config::Utility::getAndCheckFactory<InputMatcherFactory>(predicate.custom_match());
ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
predicate.custom_match().typed_config(), factory_context_.messageValidationVisitor(),
factory);
return factory.createInputMatcherFactoryCb(*message, factory_context_);
predicate.custom_match().typed_config(), proto_validation_visitor_, factory);
return factory.createInputMatcherFactoryCb(*message, proto_validation_visitor_);
}
default:
NOT_REACHED_GCOVR_EXCL_LINE;
}
}

const std::string stats_prefix_;
Server::Configuration::FactoryContext& factory_context_;
ActionFactoryContext& action_factory_context_;
ProtobufMessage::ValidationVisitor& proto_validation_visitor_;
MatchTreeValidationVisitor<DataType>& validation_visitor_;
};
} // namespace Matcher
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/matching:data_impl_lib",
"//source/common/matcher:matcher_lib",
"@envoy_api//envoy/extensions/filters/http/composite/v3:pkg_cc_proto",
],
Expand Down
3 changes: 2 additions & 1 deletion source/extensions/filters/http/composite/action.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace Composite {
void ExecuteFilterAction::createFilters(Http::FilterChainFactoryCallbacks& callbacks) const {
cb_(callbacks);
}
REGISTER_FACTORY(ExecuteFilterActionFactory, Matcher::ActionFactory);
REGISTER_FACTORY(ExecuteFilterActionFactory,
Matcher::ActionFactory<Http::Matching::HttpFilterActionContext>);
} // namespace Composite
} // namespace HttpFilters
} // namespace Extensions
Expand Down
17 changes: 10 additions & 7 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/matching/data_impl.h"
#include "source/common/matcher/matcher.h"

namespace Envoy {
Expand All @@ -21,23 +22,25 @@ class ExecuteFilterAction
Http::FilterFactoryCb cb_;
};

class ExecuteFilterActionFactory : public Matcher::ActionFactory {
class ExecuteFilterActionFactory
: public Matcher::ActionFactory<Http::Matching::HttpFilterActionContext> {
public:
std::string name() const override { return "composite-action"; }
Matcher::ActionFactoryCb
createActionFactoryCb(const Protobuf::Message& config, const std::string& stat_prefix,
Server::Configuration::FactoryContext& factory_context) override {
createActionFactoryCb(const Protobuf::Message& config,
Http::Matching::HttpFilterActionContext& context,
ProtobufMessage::ValidationVisitor& validation_visitor) override {
const auto& composite_action = MessageUtil::downcastAndValidate<
const envoy::extensions::filters::http::composite::v3::ExecuteFilterAction&>(
config, factory_context.messageValidationVisitor());
config, validation_visitor);

auto& factory =
Config::Utility::getAndCheckFactory<Server::Configuration::NamedHttpFilterConfigFactory>(
composite_action.typed_config());
ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig(
composite_action.typed_config().typed_config(), factory_context.messageValidationVisitor(),
factory);
auto callback = factory.createFilterFactoryFromProto(*message, stat_prefix, factory_context);
composite_action.typed_config().typed_config(), validation_visitor, factory);
auto callback = factory.createFilterFactoryFromProto(*message, context.stat_prefix_,
context.factory_context_);
return [cb = std::move(callback)]() -> Matcher::ActionPtr {
return std::make_unique<ExecuteFilterAction>(cb);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ namespace EnvironmentVariable {

Envoy::Matcher::CommonProtocolInputFactoryCb
Config::createCommonProtocolInputFactoryCb(const Protobuf::Message& config,
Server::Configuration::FactoryContext& factory_context) {
ProtobufMessage::ValidationVisitor& validation_visitor) {
const auto& environment_config = MessageUtil::downcastAndValidate<
const envoy::extensions::matching::common_inputs::environment_variable::v3::Config&>(
config, factory_context.messageValidationVisitor());
config, validation_visitor);

// We read the env variable at construction time to avoid repeat lookups.
// This assumes that the environment remains stable during the process lifetime.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Config : public Envoy::Matcher::CommonProtocolInputFactory {
public:
Envoy::Matcher::CommonProtocolInputFactoryCb createCommonProtocolInputFactoryCb(
const Protobuf::Message& config,
Server::Configuration::FactoryContext& factory_context) override;
ProtobufMessage::ValidationVisitor& validation_visitor) override;

std::string name() const override { return "envoy.matching.common_inputs.environment_variable"; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace InputMatchers {
namespace ConsistentHashing {

Envoy::Matcher::InputMatcherFactoryCb ConsistentHashingConfig::createInputMatcherFactoryCb(
const Protobuf::Message& config, Server::Configuration::FactoryContext& factory_context) {
const Protobuf::Message& config, ProtobufMessage::ValidationVisitor& validation_visitor) {
const auto& consistent_hashing_config =
MessageUtil::downcastAndValidate<const envoy::extensions::matching::input_matchers::
consistent_hashing::v3::ConsistentHashing&>(
config, factory_context.messageValidationVisitor());
config, validation_visitor);

if (consistent_hashing_config.threshold() > consistent_hashing_config.modulo()) {
throw EnvoyException(fmt::format("threshold cannot be greater than modulo: {} > {}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ConsistentHashingConfig : public Envoy::Matcher::InputMatcherFactory {
public:
Envoy::Matcher::InputMatcherFactoryCb
createInputMatcherFactoryCb(const Protobuf::Message& config,
Server::Configuration::FactoryContext& factory_context) override;
ProtobufMessage::ValidationVisitor& validation_visitor) override;

std::string name() const override { return "envoy.matching.matchers.consistent_hashing"; }

Expand Down
Loading

0 comments on commit be1cd7b

Please sign in to comment.