extproc: implement ClientFilter and ClientFilterBuilder interface#9086
extproc: implement ClientFilter and ClientFilterBuilder interface#9086eshitachandwani wants to merge 16 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9086 +/- ##
==========================================
- Coverage 83.20% 83.19% -0.02%
==========================================
Files 417 417
Lines 33648 33698 +50
==========================================
+ Hits 27998 28035 +37
- Misses 4235 4244 +9
- Partials 1415 1419 +4
🚀 New features to boost your workflow:
|
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces the Envoy external processing filter, providing configuration parsing logic and the infrastructure to build a client interceptor. Feedback addresses a critical uninitialized function variable that would cause a panic during filter construction and suggests a performance optimization for slice allocation when converting string matchers.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
As per offline discussion , changed override config to use the new optional type and changed the functions and tests accordingly. |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a generic Option[T] type for optional values and establishes the foundational configuration and builder logic for the xDS external processing (extproc) HTTP filter. Feedback highlights critical concerns regarding resource management, specifically the creation of unmanaged grpc.ClientConn instances that could lead to leaks and performance issues. Additionally, the interceptor struct currently embeds an uninitialized interface, which would result in a nil pointer dereference panic if invoked.
|
@eshitachandwani |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements the BuildClientFilter and BuildClientInterceptor methods for the extproc HTTP filter, along with logic to merge base and override configurations. The review identified several critical issues: embedding the resolver.ClientInterceptor interface without implementing its methods will cause a nil pointer dereference panic when invoked; creating a new grpc.ClientConn for every interceptor is inefficient and risks resource exhaustion; and the configuration merging logic performs a shallow copy of slices, which could lead to unintended side effects if the shared underlying arrays are modified.
| createExtProcChannel = func(xdsresource.GRPCServiceConfig) (*grpc.ClientConn, error) { | ||
| return nil, fmt.Errorf("dialing external processing server not implemented") | ||
| } |
There was a problem hiding this comment.
Nit: Please consider having this method return a (grpc.ClientConnInterface, func(), error) instead of (*grpc.ClientConn, error). We only care about the methods in the grpc.ClientConnInterface and Close. The latter will be satisfied by the second return value func(). The reason for making the return type be the interface is that it allows the implementer to get fancy and return a wrapped ClientConn if required.
There was a problem hiding this comment.
Done. Changed it to return (grpc.ClientConnInterface, func() error, error) . I changed it to func() error since clientconn.Close is a function that returns error.
| if err != nil { | ||
| return nil, fmt.Errorf("extproc: failed to create client: %v", err) | ||
| } | ||
| extClient := v3procservicepb.NewExternalProcessorClient(cc) |
There was a problem hiding this comment.
Why do we have to create the stub here and pass it as a separate field when we are anyways passing the cc?
There was a problem hiding this comment.
I am not sure what you mean. We need the stub to create a new RPC every time NewStream is called. We actually so not need clientconn if we have the cancel function. So changed it to have the cancel function and the v3procservicepb.ExternalProcessorClient stub.
| resolver.ClientInterceptor | ||
| config baseConfig | ||
| extClient v3procservicepb.ExternalProcessorClient | ||
| cc *grpc.ClientConn |
There was a problem hiding this comment.
Same here. Make this to be of the interface type and store the cancel func as well.
There was a problem hiding this comment.
If we have the cancel function and the v3procservicepb.ExternalProcessorClient stub , we do not need the interface. Changed it to such.
|
|
||
| func (s) TestBuildClientInterceptor(t *testing.T) { | ||
| origCreateExtProcChannel := createExtProcChannel | ||
| defer func() { createExtProcChannel = origCreateExtProcChannel }() |
There was a problem hiding this comment.
Nit/Optional: Personally I find it more readable when it is:
origFoo := foo
foo := overriddenFoo
defer func() { foo = origFoo} ()
| cfg httpfilter.FilterConfig | ||
| override httpfilter.FilterConfig | ||
| wantConfig baseConfig | ||
| wantErr string |
There was a problem hiding this comment.
I don't see much value in wantErr being a string here. Why not just a bool?
There was a problem hiding this comment.
I just always thought comparing the error strings is a better test but since its the same error message every time , I guess it does not add value here. But I have added a new failure case for when channel creation fails , so retaining the wantErr string here. Let me know if we should change it?
| if !ok { | ||
| t.Fatalf("BuildClientInterceptor() returned %T, want *interceptor", intptr) | ||
| } |
There was a problem hiding this comment.
Is this ever possible? Why not just do ic := intptr.(*interceptor)? And if there is a bug in the code that ends up causing a different interceptor type to be returned, the test will panic, which is fine.
| for _, tc := range tests { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| intptr, err := f.BuildClientInterceptor(tc.cfg, tc.override) | ||
| if tc.wantErr == "" { |
There was a problem hiding this comment.
I think this test logic is still reasonably complicated for a table driven test. Please consider splitting into success and failure cases.
| }, nil | ||
| } | ||
|
|
||
| type interceptor struct { |
There was a problem hiding this comment.
Please call this clientInterceptor. We will have a serverInterceptor soonish.
This PR is part of implementing A93: xds-ext-proc
This PR adds the builder that implements the ClientFilter and ClientFilterBuilder interface. That includes creating the interceptor config from the base and the override config. It also includes making the ext_proc channel.
THis PR has a placeholder function for converting grpcService to channel The function will be implemented later when implementing gRFC A102.
The
interceptorstruct has aresolver.ClientInterceptorembedded for now, but that will be removed in a later PR when we implement theresolver.ClientInterceptorinterface. Since the filter will not be registered and no one is calling theresolver.ClientInterceptorfunction fro extproc filter , it will not panic.#ext-proc-a93
RELEASE NOTES: None