Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ext_proc: allow override metadata without grpc_service #31544

Merged
merged 15 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
9 changes: 8 additions & 1 deletion api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ syntax = "proto3";
package envoy.extensions.filters.http.ext_proc.v3;

import "envoy/config/common/mutation_rules/v3/mutation_rules.proto";
import "envoy/config/core/v3/base.proto";
import "envoy/config/core/v3/grpc_service.proto";
import "envoy/extensions/filters/http/ext_proc/v3/processing_mode.proto";
import "envoy/type/matcher/v3/string.proto";
Expand Down Expand Up @@ -273,7 +274,7 @@ message ExtProcPerRoute {
}

// Overrides that may be set on a per-route basis
// [#next-free-field: 7]
// [#next-free-field: 8]
message ExtProcOverrides {
// Set a different processing mode for this route than the default.
ProcessingMode processing_mode = 1;
Expand Down Expand Up @@ -301,4 +302,10 @@ message ExtProcOverrides {
// config used. It is the prerogative of the control plane to ensure this
// most-specific config contains the correct final overrides.
MetadataOptions metadata_options = 6;

// Additional metadata to include into streams initiated to the ext_proc gRPC
// service. This can be used for scenarios in which additional ad hoc
// authorization headers (e.g. ``x-foo-bar: baz-key``) are to be injected or
// when a route needs to partially override inherited metadata.
repeated config.core.v3.HeaderValue grpc_metadata = 7;
sitano marked this conversation as resolved.
Show resolved Hide resolved
}
9 changes: 9 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ removed_config_or_runtime:
removed ``envoy.reloadable_features.overload_manager_error_unknown_action`` and legacy code paths.

new_features:
- area: ext_proc
change: |
Added
:ref:`metadata <envoy_v3_api_field_extensions.filters.http.ext_proc.v3.ExtProcOverrides.grpc_metadata>`
config API to allow extending inherited metadata from
:ref:`ExternalProcessor.grpc_service <envoy_v3_api_field_extensions.filters.http.ext_proc.v3.ExternalProcessor.grpc_service>`
and
:ref:`ExtProcOverrides.grpc_service <envoy_v3_api_field_extensions.filters.http.ext_proc.v3.ExtProcOverrides.grpc_service>`
with the new or updated values.
- area: aws_request_signing
change: |
Update ``aws_request_signing`` filter to support use as an upstream HTTP filter. This allows successful calculation of
Expand Down
1 change: 1 addition & 0 deletions source/extensions/filters/http/ext_proc/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ envoy_cc_library(
"//source/extensions/filters/http/common:pass_through_filter_lib",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/strings:string_view",
"@envoy_api//envoy/config/common/mutation_rules/v3:pkg_cc_proto",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/http/ext_proc/v3:pkg_cc_proto",
Expand Down
218 changes: 136 additions & 82 deletions source/extensions/filters/http/ext_proc/ext_proc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
#include "source/extensions/filters/http/ext_proc/mutation_utils.h"

#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace ExternalProcessing {
namespace {

using envoy::config::common::mutation_rules::v3::HeaderMutationRules;
using envoy::extensions::filters::http::ext_proc::v3::ExtProcPerRoute;
Expand All @@ -33,9 +35,114 @@ using Http::RequestTrailerMap;
using Http::ResponseHeaderMap;
using Http::ResponseTrailerMap;

static const std::string ErrorPrefix = "ext_proc_error";
static const int DefaultImmediateStatus = 200;
static const std::string FilterName = "envoy.filters.http.ext_proc";
constexpr absl::string_view ErrorPrefix = "ext_proc_error";
constexpr int DefaultImmediateStatus = 200;
constexpr absl::string_view FilterName = "envoy.filters.http.ext_proc";

absl::optional<ProcessingMode> initProcessingMode(const ExtProcPerRoute& config) {
if (!config.disabled() && config.has_overrides() && config.overrides().has_processing_mode()) {
return config.overrides().processing_mode();
}
return absl::nullopt;
}

absl::optional<envoy::config::core::v3::GrpcService>
initGrpcService(const ExtProcPerRoute& config) {
if (config.has_overrides() && config.overrides().has_grpc_service()) {
return config.overrides().grpc_service();
}
return absl::nullopt;
}

std::vector<std::string> initNamespaces(const Protobuf::RepeatedPtrField<std::string>& ns) {
std::vector<std::string> namespaces;
for (const auto& single_ns : ns) {
namespaces.emplace_back(single_ns);
}
return namespaces;
}

absl::optional<std::vector<std::string>>
initUntypedForwardingNamespaces(const ExtProcPerRoute& config) {
if (!config.has_overrides() || !config.overrides().has_metadata_options() ||
!config.overrides().metadata_options().has_forwarding_namespaces()) {
return absl::nullopt;
}

return {initNamespaces(config.overrides().metadata_options().forwarding_namespaces().untyped())};
}

absl::optional<std::vector<std::string>>
initTypedForwardingNamespaces(const ExtProcPerRoute& config) {
if (!config.has_overrides() || !config.overrides().has_metadata_options() ||
!config.overrides().metadata_options().has_forwarding_namespaces()) {
return absl::nullopt;
}

return {initNamespaces(config.overrides().metadata_options().forwarding_namespaces().typed())};
}

absl::optional<std::vector<std::string>>
initUntypedReceivingNamespaces(const ExtProcPerRoute& config) {
if (!config.has_overrides() || !config.overrides().has_metadata_options() ||
!config.overrides().metadata_options().has_receiving_namespaces()) {
return absl::nullopt;
}

return {initNamespaces(config.overrides().metadata_options().receiving_namespaces().untyped())};
}

absl::optional<ProcessingMode> mergeProcessingMode(const FilterConfigPerRoute& less_specific,
const FilterConfigPerRoute& more_specific) {
if (more_specific.disabled()) {
return absl::nullopt;
}
return more_specific.processingMode().has_value() ? more_specific.processingMode()
: less_specific.processingMode();
}

// Replaces all entries with the same name or append one.
void mergeHeaderValues(std::vector<envoy::config::core::v3::HeaderValue>& metadata,
const envoy::config::core::v3::HeaderValue& header) {
bool count = false;
sitano marked this conversation as resolved.
Show resolved Hide resolved
for (auto& dest : metadata) {
if (dest.key() == header.key()) {
dest.CopyFrom(header);
count = true;
}
}
if (!count) {
metadata.emplace_back(header);
}
}

std::vector<envoy::config::core::v3::HeaderValue>
mergeMetadata(const FilterConfigPerRoute& less_specific,
const FilterConfigPerRoute& more_specific) {
std::vector<envoy::config::core::v3::HeaderValue> metadata(less_specific.grpcMetadata());

for (const auto& header : more_specific.grpcMetadata()) {
mergeHeaderValues(metadata, header);
}

return metadata;
}

// Replaces all entries with the same name or append one.
void mergeHeaderValuesField(
Protobuf::RepeatedPtrField<::envoy::config::core::v3::HeaderValue>& metadata,
const envoy::config::core::v3::HeaderValue& header) {
bool count = false;
for (auto& dest : metadata) {
if (dest.key() == header.key()) {
dest.CopyFrom(header);
count = true;
}
}
if (!count) {
metadata.Add()->CopyFrom(header);
}
}

// Changes to headers are normally tested against the MutationRules supplied
// with configuration. When writing an immediate response message, however,
Expand All @@ -58,6 +165,19 @@ class ImmediateMutationChecker {
std::unique_ptr<Checker> rule_checker_;
};

const ImmediateMutationChecker& immediateResponseChecker() {
CONSTRUCT_ON_FIRST_USE(ImmediateMutationChecker);
}

ProcessingMode allDisabledMode() {
ProcessingMode pm;
pm.set_request_header_mode(ProcessingMode::SKIP);
pm.set_response_header_mode(ProcessingMode::SKIP);
return pm;
}

} // namespace

void ExtProcLoggingInfo::recordGrpcCall(
std::chrono::microseconds latency, Grpc::Status::GrpcStatus call_status,
ProcessorState::CallbackState callback_state,
Expand Down Expand Up @@ -115,77 +235,11 @@ ExtProcLoggingInfo::grpcCalls(envoy::config::core::v3::TrafficDirection traffic_
: encoding_processor_grpc_calls_;
}

std::vector<std::string>
FilterConfigPerRoute::initNamespaces(const Protobuf::RepeatedPtrField<std::string>& ns) {
if (ns.empty()) {
return {};
}

std::vector<std::string> namespaces;
for (const auto& single_ns : ns) {
namespaces.emplace_back(single_ns);
}
return namespaces;
}

absl::optional<std::vector<std::string>>
FilterConfigPerRoute::initUntypedForwardingNamespaces(const ExtProcPerRoute& config) {
if (!config.has_overrides() || !config.overrides().has_metadata_options() ||
!config.overrides().metadata_options().has_forwarding_namespaces()) {
return absl::nullopt;
}

return {initNamespaces(config.overrides().metadata_options().forwarding_namespaces().untyped())};
}

absl::optional<std::vector<std::string>>
FilterConfigPerRoute::initTypedForwardingNamespaces(const ExtProcPerRoute& config) {
if (!config.has_overrides() || !config.overrides().has_metadata_options() ||
!config.overrides().metadata_options().has_forwarding_namespaces()) {
return absl::nullopt;
}

return {initNamespaces(config.overrides().metadata_options().forwarding_namespaces().typed())};
}

absl::optional<std::vector<std::string>>
FilterConfigPerRoute::initUntypedReceivingNamespaces(const ExtProcPerRoute& config) {
if (!config.has_overrides() || !config.overrides().has_metadata_options() ||
!config.overrides().metadata_options().has_receiving_namespaces()) {
return absl::nullopt;
}

return {initNamespaces(config.overrides().metadata_options().receiving_namespaces().untyped())};
}

absl::optional<ProcessingMode>
FilterConfigPerRoute::initProcessingMode(const ExtProcPerRoute& config) {
if (!config.disabled() && config.has_overrides() && config.overrides().has_processing_mode()) {
return config.overrides().processing_mode();
}
return absl::nullopt;
}
absl::optional<envoy::config::core::v3::GrpcService>
FilterConfigPerRoute::initGrpcService(const ExtProcPerRoute& config) {
if (config.has_overrides() && config.overrides().has_grpc_service()) {
return config.overrides().grpc_service();
}
return absl::nullopt;
}

absl::optional<ProcessingMode>
FilterConfigPerRoute::mergeProcessingMode(const FilterConfigPerRoute& less_specific,
const FilterConfigPerRoute& more_specific) {
if (more_specific.disabled()) {
return absl::nullopt;
}
return more_specific.processingMode().has_value() ? more_specific.processingMode()
: less_specific.processingMode();
}

FilterConfigPerRoute::FilterConfigPerRoute(const ExtProcPerRoute& config)
: disabled_(config.disabled()), processing_mode_(initProcessingMode(config)),
grpc_service_(initGrpcService(config)),
grpc_metadata_(config.overrides().grpc_metadata().begin(),
config.overrides().grpc_metadata().end()),
untyped_forwarding_namespaces_(initUntypedForwardingNamespaces(config)),
typed_forwarding_namespaces_(initTypedForwardingNamespaces(config)),
untyped_receiving_namespaces_(initUntypedReceivingNamespaces(config)) {}
Expand All @@ -196,6 +250,7 @@ FilterConfigPerRoute::FilterConfigPerRoute(const FilterConfigPerRoute& less_spec
processing_mode_(mergeProcessingMode(less_specific, more_specific)),
grpc_service_(more_specific.grpcService().has_value() ? more_specific.grpcService()
: less_specific.grpcService()),
grpc_metadata_(mergeMetadata(less_specific, more_specific)),
untyped_forwarding_namespaces_(more_specific.untypedForwardingMetadataNamespaces().has_value()
? more_specific.untypedForwardingMetadataNamespaces()
: less_specific.untypedForwardingMetadataNamespaces()),
Expand Down Expand Up @@ -994,10 +1049,6 @@ void Filter::onFinishProcessorCalls(Grpc::Status::GrpcStatus call_status) {
encoding_state_.onFinishProcessorCall(call_status);
}

static const ImmediateMutationChecker& immediateResponseChecker() {
CONSTRUCT_ON_FIRST_USE(ImmediateMutationChecker);
}

void Filter::sendImmediateResponse(const ImmediateResponse& response) {
auto status_code = response.has_status() ? response.status().code() : DefaultImmediateStatus;
if (!MutationUtils::isValidHttpStatus(status_code)) {
Expand Down Expand Up @@ -1035,13 +1086,6 @@ void Filter::sendImmediateResponse(const ImmediateResponse& response) {
mutate_headers, grpc_status, details);
}

static ProcessingMode allDisabledMode() {
ProcessingMode pm;
pm.set_request_header_mode(ProcessingMode::SKIP);
pm.set_response_header_mode(ProcessingMode::SKIP);
return pm;
}

void Filter::mergePerRouteConfig() {
if (route_config_merged_) {
return;
Expand Down Expand Up @@ -1088,6 +1132,16 @@ void Filter::mergePerRouteConfig() {
grpc_service_ = *merged_config->grpcService();
config_with_hash_key_.setConfig(*merged_config->grpcService());
}
if (!merged_config->grpcMetadata().empty()) {
ENVOY_LOG(trace, "Overriding metadata from per-route configuration");
envoy::config::core::v3::GrpcService config = config_with_hash_key_.config();
auto ptr = config.mutable_initial_metadata();
for (const auto& header : merged_config->grpcMetadata()) {
ENVOY_LOG(trace, "Setting metadata {} = {}", header.key(), header.value());
mergeHeaderValuesField(*ptr, header);
}
config_with_hash_key_.setConfig(config);
}

// For metadata namespaces, we only override the existing value if we have a
// value from our merged config. We indicate a lack of value from the merged
Expand Down
28 changes: 7 additions & 21 deletions source/extensions/filters/http/ext_proc/ext_proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ class FilterConfigPerRoute : public Router::RouteSpecificFilterConfig {
const absl::optional<const envoy::config::core::v3::GrpcService>& grpcService() const {
return grpc_service_;
}
const std::vector<envoy::config::core::v3::HeaderValue>& grpcMetadata() const {
return grpc_metadata_;
}

const absl::optional<const std::vector<std::string>>&
untypedForwardingMetadataNamespaces() const {
Expand All @@ -260,31 +263,11 @@ class FilterConfigPerRoute : public Router::RouteSpecificFilterConfig {
}

private:
absl::optional<envoy::extensions::filters::http::ext_proc::v3::ProcessingMode>
initProcessingMode(const envoy::extensions::filters::http::ext_proc::v3::ExtProcPerRoute& config);

absl::optional<envoy::config::core::v3::GrpcService>
initGrpcService(const envoy::extensions::filters::http::ext_proc::v3::ExtProcPerRoute& config);

std::vector<std::string> initNamespaces(const Protobuf::RepeatedPtrField<std::string>& ns);

absl::optional<std::vector<std::string>> initUntypedForwardingNamespaces(
const envoy::extensions::filters::http::ext_proc::v3::ExtProcPerRoute& config);

absl::optional<std::vector<std::string>> initTypedForwardingNamespaces(
const envoy::extensions::filters::http::ext_proc::v3::ExtProcPerRoute& config);

absl::optional<std::vector<std::string>> initUntypedReceivingNamespaces(
const envoy::extensions::filters::http::ext_proc::v3::ExtProcPerRoute& config);

absl::optional<envoy::extensions::filters::http::ext_proc::v3::ProcessingMode>
mergeProcessingMode(const FilterConfigPerRoute& less_specific,
const FilterConfigPerRoute& more_specific);

const bool disabled_;
const absl::optional<const envoy::extensions::filters::http::ext_proc::v3::ProcessingMode>
processing_mode_;
const absl::optional<const envoy::config::core::v3::GrpcService> grpc_service_;
std::vector<envoy::config::core::v3::HeaderValue> grpc_metadata_;

const absl::optional<const std::vector<std::string>> untyped_forwarding_namespaces_;
const absl::optional<const std::vector<std::string>> typed_forwarding_namespaces_;
Expand Down Expand Up @@ -321,6 +304,9 @@ class Filter : public Logger::Loggable<Logger::Id::ext_proc>,
config->untypedReceivingMetadataNamespaces()) {}

const FilterConfig& config() const { return *config_; }
const envoy::config::core::v3::GrpcService& grpc_service_config() const {
return config_with_hash_key_.config();
}

ExtProcFilterStats& stats() { return stats_; }
ExtProcLoggingInfo* loggingInfo() { return logging_info_; }
Expand Down
Loading
Loading