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
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,5 @@ extensions/filters/http/oauth2 @rgs1 @derekargueta @snowp
# Original IP detection
/*/extensions/http/original_ip_detection/custom_header @rgs1 @alyssawilk @antoniovicente
/*/extensions/http/original_ip_detection/xff @rgs1 @alyssawilk @antoniovicente
# set_metadata extension
/*/extensions/filters/http/set_metadata @aguinet @snowp
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ proto_library(
"//envoy/extensions/filters/http/ratelimit/v3:pkg",
"//envoy/extensions/filters/http/rbac/v3:pkg",
"//envoy/extensions/filters/http/router/v3:pkg",
"//envoy/extensions/filters/http/set_metadata/v3:pkg",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to added myself, but it asked for a second maintainer. Who should I add? @snowp as he is sponsoring it :) ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Snow and you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry missed this answer. Done in d139579 !

"//envoy/extensions/filters/http/squash/v3:pkg",
"//envoy/extensions/filters/http/tap/v3:pkg",
"//envoy/extensions/filters/http/wasm/v3:pkg",
Expand Down
9 changes: 9 additions & 0 deletions api/envoy/extensions/filters/http/set_metadata/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
syntax = "proto3";

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

import "google/protobuf/struct.proto";

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

option java_package = "io.envoyproxy.envoy.extensions.filters.http.set_metadata.v3";
option java_outer_classname = "SetMetadataProto";
option java_multiple_files = true;
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Set-Metadata Filter]
//
// This filters adds or updates dynamic metadata with static data.
//
// [#extension: envoy.filters.http.set_metadata]

message Config {
// The metadata namespace.
string metadata_namespace = 1 [(validate.rules).string = {min_len: 1}];

// The value to update the namespace with. See
// :ref:`the filter documentation <config_http_filters_set_metadata>` for
// more information on how this value is merged with potentially existing
// ones.
google.protobuf.Struct value = 2;
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ proto_library(
"//envoy/extensions/filters/http/ratelimit/v3:pkg",
"//envoy/extensions/filters/http/rbac/v3:pkg",
"//envoy/extensions/filters/http/router/v3:pkg",
"//envoy/extensions/filters/http/set_metadata/v3:pkg",
"//envoy/extensions/filters/http/squash/v3:pkg",
"//envoy/extensions/filters/http/tap/v3:pkg",
"//envoy/extensions/filters/http/wasm/v3:pkg",
Expand Down
1 change: 1 addition & 0 deletions docs/root/configuration/http/http_filters/http_filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ HTTP filters
rate_limit_filter
rbac_filter
router_filter
set_metadata_filter
squash_filter
tap_filter
wasm_filter
Expand Down
52 changes: 52 additions & 0 deletions docs/root/configuration/http/http_filters/set_metadata_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.. _config_http_filters_set_metadata:

Set Metadata
============
* :ref:`v3 API reference <envoy_v3_api_msg_extensions.filters.http.set_metadata.v3.Config>`
* This filter should be configured with the name *envoy.filters.http.set_metadata*.

This filters adds or updates dynamic metadata with static data.

Dynamic metadata values are updated with the following scheme. If a key
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@envoyproxy/api-shepherds this differs from MergeFrom in that it supports merging structs together, e.g. if you have a disjoint namespace things compose as expected. I think this makes sense, but flagging this for consideration in case there is some alternate merging preferences.

does not exists, it's just copied into the current metadata. If the key exists
but has a different type, it is replaced by the new value. Otherwise:

* for scalar values (null, string, number, boolean) are replaced with the new value
* for lists: new values are added to the current list
* for structures: recursively apply this scheme

For instance, if the namespace already contains this structure:

.. code-block:: yaml

myint: 1
mylist: ["a"]
mykey: ["val"]
mytags:
tag0: 1

and the value to set is:

.. code-block:: yaml

myint: 2
mylist: ["b","c"]
mykey: 1
mytags:
tag1: 1

After applying this filter, the namespace will contain:

.. code-block:: yaml

myint: 2
mylist: ["a","b","c"]
mykey: 1
mytags:
tag0: 1
tag1: 1

Statistics
----------

Currently, this filter generates no statistics.
1 change: 1 addition & 0 deletions generated_api_shadow/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ proto_library(
"//envoy/extensions/filters/http/ratelimit/v3:pkg",
"//envoy/extensions/filters/http/rbac/v3:pkg",
"//envoy/extensions/filters/http/router/v3:pkg",
"//envoy/extensions/filters/http/set_metadata/v3:pkg",
"//envoy/extensions/filters/http/squash/v3:pkg",
"//envoy/extensions/filters/http/tap/v3:pkg",
"//envoy/extensions/filters/http/wasm/v3:pkg",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions source/common/protobuf/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1056,4 +1056,43 @@ std::string TypeUtil::descriptorFullNameToTypeUrl(absl::string_view type) {
return "type.googleapis.com/" + std::string(type);
}

void StructUtil::update(ProtobufWkt::Struct& obj, const ProtobufWkt::Struct& with) {
auto& obj_fields = *obj.mutable_fields();

for (const auto& [key, val] : with.fields()) {
auto& obj_key = obj_fields[key];

// If the types are different, the last one wins.
const auto val_kind = val.kind_case();
if (val_kind != obj_key.kind_case()) {
obj_key = val;
continue;
}

// Otherwise, the strategy depends on the value kind.
switch (val.kind_case()) {
// For scalars, the last one wins.
case ProtobufWkt::Value::kNullValue:
case ProtobufWkt::Value::kNumberValue:
case ProtobufWkt::Value::kStringValue:
case ProtobufWkt::Value::kBoolValue:
obj_key = val;
break;
// If we got a structure, recursively update.
case ProtobufWkt::Value::kStructValue:
update(*obj_key.mutable_struct_value(), val.struct_value());
break;
// For lists, append the new values.
case ProtobufWkt::Value::kListValue: {
auto& obj_key_vec = *obj_key.mutable_list_value()->mutable_values();
const auto& vals = val.list_value().values();
obj_key_vec.MergeFrom(vals);
break;
}
case ProtobufWkt::Value::KIND_NOT_SET:
break;
}
}
}

} // namespace Envoy
19 changes: 19 additions & 0 deletions source/common/protobuf/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,25 @@ class TimestampUtil {
ProtobufWkt::Timestamp& timestamp);
};

class StructUtil {
public:
/**
* Recursively updates in-place a protobuf structure with keys from another
* object.
*
* The merging strategy is the following. If a key from \p other does not
* exists, it's just copied into \p obj. If the key exists but has a
* different type, it is replaced by the new value. Otherwise:
* - for scalar values (null, string, number, boolean) are replaced with the new value
* - for lists: new values are added to the current list
* - for structures: recursively apply this scheme
*
* @param obj the object to update in-place
* @param with the object to update \p obj with
*/
static void update(ProtobufWkt::Struct& obj, const ProtobufWkt::Struct& with);
};

} // namespace Envoy

namespace std {
Expand Down
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ EXTENSIONS = {
"envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config",
"envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config",
"envoy.filters.http.router": "//source/extensions/filters/http/router:config",
"envoy.filters.http.set_metadata": "//source/extensions/filters/http/set_metadata:config",
"envoy.filters.http.squash": "//source/extensions/filters/http/squash:config",
"envoy.filters.http.tap": "//source/extensions/filters/http/tap:config",
"envoy.filters.http.wasm": "//source/extensions/filters/http/wasm:config",
Expand Down
40 changes: 40 additions & 0 deletions source/extensions/filters/http/set_metadata/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_extension",
"envoy_cc_library",
"envoy_extension_package",
)

licenses(["notice"]) # Apache 2

envoy_extension_package()

envoy_cc_library(
name = "set_metadata_filter_lib",
srcs = ["set_metadata_filter.cc"],
hdrs = ["set_metadata_filter.h"],
deps = [
"//include/envoy/server:filter_config_interface",
"//source/common/http:utility_lib",
"//source/common/protobuf:utility_lib",
"//source/extensions/filters/http:well_known_names",
"//source/extensions/filters/http/common:pass_through_filter_lib",
"@envoy_api//envoy/extensions/filters/http/set_metadata/v3:pkg_cc_proto",
],
)

envoy_cc_extension(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
category = "envoy.filters.http",
security_posture = "robust_to_untrusted_downstream_and_upstream",
deps = [
"//include/envoy/registry",
"//source/common/protobuf:utility_lib",
"//source/extensions/filters/http:well_known_names",
"//source/extensions/filters/http/common:factory_base_lib",
"//source/extensions/filters/http/set_metadata:set_metadata_filter_lib",
"@envoy_api//envoy/extensions/filters/http/set_metadata/v3:pkg_cc_proto",
],
)
34 changes: 34 additions & 0 deletions source/extensions/filters/http/set_metadata/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "extensions/filters/http/set_metadata/config.h"

#include <string>

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

#include "common/protobuf/utility.h"

#include "extensions/filters/http/set_metadata/set_metadata_filter.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace SetMetadataFilter {

Http::FilterFactoryCb SetMetadataConfig::createFilterFactoryFromProtoTyped(
const envoy::extensions::filters::http::set_metadata::v3::Config& proto_config,
const std::string&, Server::Configuration::FactoryContext&) {
ConfigSharedPtr filter_config(std::make_shared<Config>(proto_config));

return [filter_config](Http::FilterChainFactoryCallbacks& callbacks) -> void {
callbacks.addStreamDecoderFilter(
Http::StreamDecoderFilterSharedPtr{new SetMetadataFilter(filter_config)});
};
}

REGISTER_FACTORY(SetMetadataConfig, Server::Configuration::NamedHttpFilterConfigFactory);

} // namespace SetMetadataFilter
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
31 changes: 31 additions & 0 deletions source/extensions/filters/http/set_metadata/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

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

#include "extensions/filters/http/common/factory_base.h"
#include "extensions/filters/http/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace SetMetadataFilter {

/**
* Config registration for the header-to-metadata filter. @see NamedHttpFilterConfigFactory.
*/
class SetMetadataConfig
: public Common::FactoryBase<envoy::extensions::filters::http::set_metadata::v3::Config> {
public:
SetMetadataConfig() : FactoryBase(HttpFilterNames::get().SetMetadata) {}

private:
Http::FilterFactoryCb createFilterFactoryFromProtoTyped(
const envoy::extensions::filters::http::set_metadata::v3::Config& proto_config,
const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override;
};

} // namespace SetMetadataFilter
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
Loading