Skip to content

extproc: register filter and parse base and override config#9073

Open
eshitachandwani wants to merge 20 commits into
grpc:masterfrom
eshitachandwani:parsing_proc
Open

extproc: register filter and parse base and override config#9073
eshitachandwani wants to merge 20 commits into
grpc:masterfrom
eshitachandwani:parsing_proc

Conversation

@eshitachandwani
Copy link
Copy Markdown
Member

This PR is part of implementing A93: xds-ext-proc

This PR adds the ext_proc filter and the builder that implements the builder interface. That includes parsing and validating the base and the override config. The registration of the filter is under the GRPC_EXPERIMENTAL_XDS_EXT_PROC_ON_CLIENT flag.

#ext-proc-a93

RELEASE NOTES: None

@eshitachandwani eshitachandwani added this to the 1.82 Release milestone Apr 17, 2026
@eshitachandwani eshitachandwani added the Type: Feature New features or improvements in behavior label Apr 17, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 17, 2026

Codecov Report

❌ Patch coverage is 76.51007% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.09%. Comparing base (06fc26a) to head (9c30b51).
⚠️ Report is 29 commits behind head on master.

Files with missing lines Patch % Lines
internal/xds/httpfilter/extproc/ext_proc.go 72.22% 19 Missing and 6 partials ⚠️
internal/xds/httpfilter/extproc/config.go 68.00% 8 Missing ⚠️
internal/xds/httpfilter/extconfig.go 92.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9073      +/-   ##
==========================================
+ Coverage   80.52%   83.09%   +2.56%     
==========================================
  Files         413      417       +4     
  Lines       33543    33648     +105     
==========================================
+ Hits        27012    27959     +947     
+ Misses       4316     4259      -57     
+ Partials     2215     1430     -785     
Files with missing lines Coverage Δ
internal/optional/optional.go 100.00% <100.00%> (ø)
internal/xds/httpfilter/extconfig.go 92.00% <92.00%> (ø)
internal/xds/httpfilter/extproc/config.go 68.00% <68.00%> (ø)
internal/xds/httpfilter/extproc/ext_proc.go 72.22% <72.22%> (ø)

... and 61 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mbissa
Copy link
Copy Markdown
Contributor

mbissa commented Apr 17, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements the Envoy external processing (ext_proc) HTTP filter for xDS, including configuration parsing for base and override settings. The reviewer noted that the implementation incorrectly uses a non-existent GRPC body processing mode instead of STREAMED and lacks support for TypedStruct types in the configuration parser. As a result, the validation logic and unit tests require updates to correctly handle supported processing modes and additional configuration formats.

Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/config_test.go
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
@mbissa
Copy link
Copy Markdown
Contributor

mbissa commented Apr 20, 2026

LGTM, barring the udpa.type.v1.TypedStruct and xds.type.v3.TypedStruct support comment from gemini. Can go ahead if we get an answer on those.

@eshitachandwani
Copy link
Copy Markdown
Member Author

LGTM, barring the udpa.type.v1.TypedStruct and xds.type.v3.TypedStruct support comment from gemini. Can go ahead if we get an answer on those.

It has been discussed that we only have to support udpa.type.v1.TypedStruct and xds.type.v3.TypedStruct for third-party filters and need not be supported for filters already in xDS

Comment thread internal/xds/httpfilter/extproc/config.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
@easwars easwars assigned eshitachandwani and unassigned easwars Apr 21, 2026
@eshitachandwani
Copy link
Copy Markdown
Member Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a generic Option[T] type and implements the configuration parsing logic for the xDS external processing filter, including structures for base and override configurations. Critical feedback highlights potential runtime panics due to missing nil checks on protobuf message types and the ExtProcPerRoute override field. Other suggestions address misleading error prefixes in shared utilities, unnecessary whitespace, and various typos in comments and error messages.

Comment thread internal/xds/httpfilter/extconfig.go
Comment thread internal/xds/httpfilter/extproc/ext_proc.go
Comment thread experimental/optional/optional.go Outdated
Comment thread internal/xds/httpfilter/extconfig.go Outdated
Comment thread internal/xds/httpfilter/extconfig.go Outdated
Comment thread internal/xds/httpfilter/extproc/config.go Outdated
Comment thread internal/xds/httpfilter/extproc/config.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/config.go Outdated
Comment thread experimental/optional/optional.go Outdated
Comment thread internal/optional/optional.go Outdated
Comment thread experimental/optional/optional.go Outdated
Comment thread internal/optional/optional.go Outdated
Comment thread internal/xds/httpfilter/extconfig.go Outdated
Comment on lines +59 to +64
// ChannelCredentials specifies the transport credentials to use to connect to
// the external server. Must not be nil.
ChannelCredentials json.RawMessage
// CallCredentials specifies the per-RPC credentials to use when making calls
// to the external server.
CallCredentials []json.RawMessage
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These fields are still not comparable. We need it to be a string. Byte slices are not comparable. Please add a test to ensure that you can store this field in a map.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Changed to string. But ServerConfig as a whole is still not comparable and cannot be used as a key because :

  1. metadata is a map which is not comparable
  2. We do not want to compare the timeout and metadata as they are set per RPC , not per channel.
    I think we should move these fields out of ServerConfig and into the InterceptorConfig. WDYT ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please clarify that the string value for ChannelCredentials and CallCredentials specifies their configuration as a JSON string.

I think we should move these fields out of ServerConfig and into the InterceptorConfig. WDYT ?
I don't think so, because we might have other users (who are not filters) that want those values.

Let's consider storing metadata as []HeaderValue where HeaderValue is defined as:

type HeaderValue struct {
  Key string
  Value string // is set when `Key` does not end with a `-bin` suffix
  RawValue string // Base64 encoded value of the header when `Key` ends with a `-bin` suffix
}

Will this work?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Slices are also not directly comparable. We can have an Equal or EqualChannel method on the ServiceConfig and in that we can just compare the targetURI and the creds and leave out comparing the metadata. Then we can use the whole ServerConfig as the key. If we do this, we can also use bootstrap.ChannelCredentials and bootstrap.CallCredentials defined here , directly since they have an Equal method already defined. WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Where is the Equal method that you are talking about?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oops, attatched the wrong type and file. I was talking about using this type

Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc_test.go Outdated
@easwars easwars assigned eshitachandwani and unassigned easwars May 12, 2026
Comment thread experimental/optional/optional.go Outdated
Comment thread internal/optional/optional.go Outdated
Comment thread internal/optional/optional.go Outdated
Comment thread internal/optional/optional.go Outdated
Comment thread internal/xds/httpfilter/extconfig.go Outdated
Comment on lines +59 to +64
// ChannelCredentials specifies the transport credentials to use to connect to
// the external server. Must not be nil.
ChannelCredentials json.RawMessage
// CallCredentials specifies the per-RPC credentials to use when making calls
// to the external server.
CallCredentials []json.RawMessage
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please clarify that the string value for ChannelCredentials and CallCredentials specifies their configuration as a JSON string.

I think we should move these fields out of ServerConfig and into the InterceptorConfig. WDYT ?
I don't think so, because we might have other users (who are not filters) that want those values.

Let's consider storing metadata as []HeaderValue where HeaderValue is defined as:

type HeaderValue struct {
  Key string
  Value string // is set when `Key` does not end with a `-bin` suffix
  RawValue string // Base64 encoded value of the header when `Key` ends with a `-bin` suffix
}

Will this work?

}

// resolveHeaderMode resolves the processing mode for headers based on the
// protobuf enum value. If the mode is not set or set to Default, it returns the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we say ProcessingMode_DEFAULT in that case to be clear?

Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/optional/optional_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/config.go Outdated
Comment thread internal/xds/httpfilter/extproc/config.go
return modeSend
case v3procfilterpb.ProcessingMode_SKIP:
return modeSkip
default:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, it might be better here and down in resolveBodyMode to have a case for ProcessingMode_DEFAULT and not have a default case. That way, if a new enum value is added, we don't silently treat it like the default.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ping

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

added a ProcessingMode_DEFAULT in the resolveHeaderMode but body mode does not have a ProcessingMode_DEFAULT and we need default statement because : As mentioned in the official GO documentation here , in point 6 , Switch is only terminating if it has a default case and all the cases including default has a return statement.

Comment thread internal/xds/httpfilter/extconfig.go Outdated
Comment thread internal/xds/httpfilter/extconfig.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/config_test.go Outdated
Comment thread internal/xds/httpfilter/extproc/config_test.go Outdated
@easwars easwars assigned eshitachandwani and unassigned easwars and mbissa May 15, 2026
grpctest.RunSubTests(t, s{})
}

func testParseGRPCServiceConfig(grpcService *corepb.GrpcService) (xdsresource.GRPCServiceConfig, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you please add a comment for this function, and mention that this is probably a temporary one until A102 is implemented.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done.

Comment on lines +32 to +37
// ChannelCredentials specifies the configuration for the transport
// credentials to use to connect to the external server, as a JSON string.
ChannelCredentials string
// CallCredentials specifies the configuration for the per-RPC credentials to
// use when making calls to the external server, as a JSON string.
CallCredentials string
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we are going to go with the approach where we are not going to bother to make this type be comparable, then, we should consider making the channel credentials be of type credentials.Bundle and the call credentials be of type credentials.PerRPCCredentials. (Note: see the xds/bootstrap package API for why we need a credentials.Bundle and not a credentials.TransportCredentials).

Then, as part of A102, when we parse the grpc_service proto into this struct, we can store a wrapped struct for the above fields, which embeds the credentials, but also stores the actual JSON configuration for the credentials.

And then, there needs to be an Equal method implemented here, that can actually compare the JSON configs of the credentials as part of the equality check.

What do you think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We can do that , but from what I could understand, we agreed for this to be part of A102 implementation , i.e. if we are already going to have a functionality that will take in the ServiceConfig and give us the grpc channel , behind the scene it will have ad to have some sort of mapping for the string and the actual transportCredentials. So I was thinking that we can keep the channelCreds and call creds as strings , and we can take the values from here to create a channelKey in the filter (or wherever needed) and directly compare them. But we can have an Equal method incase anyone wants to compare the whole ServiceConfig.

But what you suggested also makes sense if we first get the whole serviceConfig and then we make a copy of it where metadata and timeout are nil values and we use that as a key and then we can directly use the Equal method defined for the whole struct.

Let me know what you think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Feature New features or improvements in behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants