diff --git a/BUILD b/BUILD index d384bf90..201c89aa 100644 --- a/BUILD +++ b/BUILD @@ -177,6 +177,7 @@ proto_library( "//envoy/extensions/filters/http/file_system_buffer/v3:pkg", "//envoy/extensions/filters/http/gcp_authn/v3:pkg", "//envoy/extensions/filters/http/geoip/v3:pkg", + "//envoy/extensions/filters/http/grpc_field_extraction/v3:pkg", "//envoy/extensions/filters/http/grpc_http1_bridge/v3:pkg", "//envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3:pkg", "//envoy/extensions/filters/http/grpc_json_transcoder/v3:pkg", diff --git a/envoy/extensions/filters/http/grpc_field_extraction/v3/BUILD b/envoy/extensions/filters/http/grpc_field_extraction/v3/BUILD new file mode 100644 index 00000000..e9b556d6 --- /dev/null +++ b/envoy/extensions/filters/http/grpc_field_extraction/v3/BUILD @@ -0,0 +1,13 @@ +# 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 = [ + "//envoy/config/core/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + "@com_github_cncf_udpa//xds/annotations/v3:pkg", + ], +) diff --git a/envoy/extensions/filters/http/grpc_field_extraction/v3/config.proto b/envoy/extensions/filters/http/grpc_field_extraction/v3/config.proto new file mode 100644 index 00000000..28f5cc15 --- /dev/null +++ b/envoy/extensions/filters/http/grpc_field_extraction/v3/config.proto @@ -0,0 +1,148 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.grpc_field_extraction.v3; + +import "envoy/config/core/v3/base.proto"; + +import "xds/annotations/v3/status.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.grpc_field_extraction.v3"; +option java_outer_classname = "ConfigProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_field_extraction/v3;grpc_field_extractionv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; +option (xds.annotations.v3.file_status).work_in_progress = true; + +// [#not-implemented-hide:] +// [#protodoc-title: gRPC Field Extraction] +// gRPC Field Extraction :ref:`configuration overview +// `. +// [#extension: envoy.filters.http.grpc_field_extraction] + +// GrpcFieldExtraction filter supports extracting the fields from the first gRPC +// request message no matter if it is unary or streaming and writing the result +// to the destination, for which currently only the static Envoy dynamic metadata `envoy.filters.http.grpc_field_extraction` is supported. +// +// # Assumptions +// 1. this filter is only applicable for gRPC with Protobuf as payload. +// 2. for bi-directional and client-side gRPC streaming, the initial message from the client should not depend on receiving the server initial metadata. +// +// # Process Flow +// When a request reaches the filter, it will check +// 1. if the request is gRPC request with a protobuf body, the filter tries to: +// a. block the incoming data before decoding the first complete gRPC message +// b. look up the target field from the buffered gRPC message +// c. if the extraction result isn't empty, write it into the dynamic metadata and resume the request propagation. +// 2. otherwise, pass through the request. +// +// If the request is a malformed one found during 1.a or 1.b, the filter will reject the request. +// +// # Config Requirements +// 1. the target field should be of a singular primitive type or a repeated primitive type +// and its value will be extracted in string format. +// 2. the intermediate type could also be repeated. +// +// # Output Format +// The result format will be `field` -> `values` in the dynamic metadata. +// +// # Performance +// This filter should be performant as it +// 1. converts between the gRPC message from EnvoyBuffer without data copy. +// 2. parse the gRPC message binary directly without deserialization. +// though buffering the first message introduces some latency. +// +// # Example, +// we have the following request definition for the gRPC method `pkg.svc.Method`. +// +// message MethodRequest { +// string foo = 1; +// Nested nested = 2; +// ... +// } +// +// message Nested { +// repeated string bar = 1; +// } +// +// This is the filter config(expressed in JSON format). +// { +// "descriptor_set":{...}, +// "extractions_by_method": { +// "pkg.svc.Method":{ +// "request_field_extractions":{ +// "foo":{ +// }, +// "nested.bar":{ +// } +// } +// } +// }, +// ... +// } +// +// +// During runtime, the filter receives the following `MethodRequest` message(expressed in JSON format). +// { +// foo: "val_foo", +// nested: { "bar": ["val_bar1", "val_bar2"]} +// } +// +// The filter will write the following dynamic metadata(expressed in JSON format). +// +// `envoy.filters.http.grpc_field_extraction`: { +// "foo":[ +// "val_foo" +// ], +// "nested.bar":[ +// "val_bar1", "val_bar2" +// ] +// } + +message GrpcFieldExtractionConfig { + // The proto descriptor set binary for the gRPC services. + // + // It could be passed by a local file through `Datasource.filename` or embedded in the + // `Datasource.inline_bytes`. + config.core.v3.DataSource descriptor_set = 1 [(validate.rules).message = {required: true}]; + + // Specify the extraction info. + // The key is the fully qualified gRPC method name. + // `${package}.${Service}.${Method}`, like + // `endpoints.examples.bookstore.BookStore.GetShelf` + // + // The value is the field extractions for individual gRPC method. + map extractions_by_method = 2; +} + +// This message can be used to support per route config approach later even +// though the Istio doesn't support that so far. +message FieldExtractions { + // The field extractions for requests. + // The key is the field path within the grpc request. + // For example, we can define `foo.bar.name` if we want to extract + // Request.foo.bar.name. + // message Request { + // // The namespace in which the Workspace should be created. + // Foo foo = 1; + // } + // + // message Foo { + // Bar bar = 1; + // } + // message Bar { + // string name = 1; + // } + map request_field_extractions = 1; +} + +message RequestFieldValueDisposition { + oneof disposition { + // The dynamic metadata namespace. If empty, "envoy.filters.http.grpc_field_extraction" will be used by default. + // + // Unimplemented. Uses "envoy.filters.http.grpc_field_extraction" for now. + string dynamic_metadata = 1; + } +} diff --git a/versioning/BUILD b/versioning/BUILD index 250fb3bf..52f5060b 100644 --- a/versioning/BUILD +++ b/versioning/BUILD @@ -115,6 +115,7 @@ proto_library( "//envoy/extensions/filters/http/file_system_buffer/v3:pkg", "//envoy/extensions/filters/http/gcp_authn/v3:pkg", "//envoy/extensions/filters/http/geoip/v3:pkg", + "//envoy/extensions/filters/http/grpc_field_extraction/v3:pkg", "//envoy/extensions/filters/http/grpc_http1_bridge/v3:pkg", "//envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3:pkg", "//envoy/extensions/filters/http/grpc_json_transcoder/v3:pkg",