-
Notifications
You must be signed in to change notification settings - Fork 4.7k
extproc: implement ClientFilter and ClientFilterBuilder interface #9086
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
base: master
Are you sure you want to change the base?
Changes from 14 commits
0542270
d31b4c9
d0d83d3
02e1edb
f36a295
328deca
3319dae
7ada0ba
f3e7083
87fb4c7
76f6f0a
a1ebc59
1dbeeed
5b914d3
00fb76b
1db23e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,12 +23,17 @@ import ( | |
| "regexp" | ||
| "strings" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/google/go-cmp/cmp" | ||
| "google.golang.org/grpc" | ||
| "google.golang.org/grpc/credentials/insecure" | ||
| "google.golang.org/grpc/internal/grpctest" | ||
| "google.golang.org/grpc/internal/optional" | ||
| "google.golang.org/grpc/internal/xds/httpfilter" | ||
| "google.golang.org/grpc/internal/xds/matcher" | ||
| "google.golang.org/grpc/internal/xds/xdsclient/xdsresource" | ||
| "google.golang.org/grpc/metadata" | ||
| "google.golang.org/protobuf/proto" | ||
| "google.golang.org/protobuf/testing/protocmp" | ||
| "google.golang.org/protobuf/types/known/anypb" | ||
|
|
@@ -48,6 +53,15 @@ func Test(t *testing.T) { | |
| grpctest.RunSubTests(t, s{}) | ||
| } | ||
|
|
||
| const testBaseURI = "base-uri" | ||
|
|
||
| // incorrectFilterConfig embeds httpfilter.FilterConfig but is not of type | ||
| // baseConfig/overrideConfig, and is used to test incorrect config types being | ||
| // passed to BuildClientInterceptor. | ||
| type incorrectFilterConfig struct { | ||
| httpfilter.FilterConfig | ||
| } | ||
|
|
||
| // testParseGRPCServiceConfig is a helper function that parses a GrpcService | ||
| // proto message into a GRPCServiceConfig. This is a temporary test | ||
| // implementation that will be removed once gRFC A102 is implemented. | ||
|
|
@@ -86,6 +100,9 @@ var cmpOpts = []cmp.Option{ | |
| } | ||
| return r.String() | ||
| }), | ||
| cmp.Comparer(func(x, y matcher.StringMatcher) bool { | ||
| return x.Equal(y) | ||
| }), | ||
| } | ||
|
|
||
| func (s) TestParseFilterConfig_Success(t *testing.T) { | ||
|
|
@@ -515,3 +532,260 @@ func (s) TestParseFilterConfigOverride_Errors(t *testing.T) { | |
| }) | ||
| } | ||
| } | ||
|
|
||
| func (s) TestBuildClientInterceptor(t *testing.T) { | ||
| origCreateExtProcChannel := createExtProcChannel | ||
| defer func() { createExtProcChannel = origCreateExtProcChannel }() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit/Optional: Personally I find it more readable when it is:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| createExtProcChannel = func(cfg xdsresource.GRPCServiceConfig) (*grpc.ClientConn, error) { | ||
| return grpc.NewClient(cfg.TargetURI, grpc.WithTransportCredentials(insecure.NewCredentials())) | ||
| } | ||
|
|
||
| b := builder{} | ||
| f := b.BuildClientFilter() | ||
| defer f.Close() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| cfg httpfilter.FilterConfig | ||
| override httpfilter.FilterConfig | ||
| wantConfig baseConfig | ||
| wantErr string | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see much value in
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 |
||
| }{ | ||
| { | ||
| name: "NilConfig", | ||
| cfg: nil, | ||
| wantErr: "extproc: incorrect config type provided", | ||
| }, | ||
| { | ||
| name: "IncorrectConfigType", | ||
| cfg: incorrectFilterConfig{}, | ||
| wantErr: "extproc: incorrect config type provided", | ||
| }, | ||
| { | ||
| name: "IncorrectOverrideType", | ||
| cfg: baseConfig{}, | ||
| override: incorrectFilterConfig{}, | ||
| wantErr: "extproc: incorrect override config type provided", | ||
| }, | ||
| { | ||
| name: "ConfigUsingOnlyBase", | ||
| cfg: baseConfig{ | ||
| failureModeAllow: true, | ||
| requestAttributes: []string{"attr1"}, | ||
| responseAttributes: []string{"attr2"}, | ||
| observabilityMode: true, | ||
| disableImmediateResponse: true, | ||
| deferredCloseTimeout: 10 * time.Second, | ||
| processingModes: processingModes{ | ||
| requestHeaderMode: modeSend, | ||
| responseHeaderMode: modeSkip, | ||
| responseTrailerMode: modeSend, | ||
| requestBodyMode: modeSend, | ||
| responseBodyMode: modeSkip, | ||
| }, | ||
| server: xdsresource.GRPCServiceConfig{ | ||
| TargetURI: testBaseURI, | ||
| ChannelCredentials: "test-channel-creds", | ||
| CallCredentials: "test-call-creds", | ||
| InitialMetadata: metadata.MD(metadata.Pairs("key1", "value1")), | ||
| Timeout: 5 * time.Second, | ||
| }, | ||
| mutationRules: httpfilter.HeaderMutationRules{ | ||
| AllowExpr: regexp.MustCompile("allow-.*"), | ||
| DisallowExpr: regexp.MustCompile("disallow-.*"), | ||
| DisallowAll: true, | ||
| DisallowIsError: true, | ||
| }, | ||
| allowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("allow-header", false)}, | ||
| }, | ||
| wantConfig: baseConfig{ | ||
| failureModeAllow: true, | ||
| requestAttributes: []string{"attr1"}, | ||
| responseAttributes: []string{"attr2"}, | ||
| mutationRules: httpfilter.HeaderMutationRules{ | ||
| AllowExpr: regexp.MustCompile("allow-.*"), | ||
| DisallowExpr: regexp.MustCompile("disallow-.*"), | ||
| DisallowAll: true, | ||
| DisallowIsError: true, | ||
| }, | ||
| observabilityMode: true, | ||
| disableImmediateResponse: true, | ||
| deferredCloseTimeout: 10 * time.Second, | ||
| processingModes: processingModes{ | ||
| requestHeaderMode: modeSend, | ||
| responseHeaderMode: modeSkip, | ||
| responseTrailerMode: modeSend, | ||
| requestBodyMode: modeSend, | ||
| responseBodyMode: modeSkip, | ||
| }, | ||
| server: xdsresource.GRPCServiceConfig{ | ||
| TargetURI: testBaseURI, | ||
| ChannelCredentials: "test-channel-creds", | ||
| CallCredentials: "test-call-creds", | ||
| InitialMetadata: metadata.MD(metadata.Pairs("key1", "value1")), | ||
| Timeout: 5 * time.Second, | ||
| }, | ||
| allowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("allow-header", false)}, | ||
| }, | ||
| }, | ||
| { | ||
| name: "ConfigUsingBaseAndOverride", | ||
| cfg: baseConfig{ | ||
| failureModeAllow: false, | ||
| requestAttributes: []string{"base-attr1"}, | ||
| responseAttributes: []string{"base-attr2"}, | ||
| observabilityMode: true, | ||
| disableImmediateResponse: true, | ||
| processingModes: processingModes{ | ||
| requestHeaderMode: modeSend, | ||
| responseHeaderMode: modeSkip, | ||
| responseTrailerMode: modeSend, | ||
| requestBodyMode: modeSend, | ||
| responseBodyMode: modeSkip, | ||
| }, | ||
| server: xdsresource.GRPCServiceConfig{ | ||
| TargetURI: testBaseURI, | ||
| Timeout: time.Second, | ||
| InitialMetadata: metadata.MD(metadata.Pairs("key1", "value1")), | ||
| }, | ||
| mutationRules: httpfilter.HeaderMutationRules{ | ||
| AllowExpr: regexp.MustCompile("allow-.*"), | ||
| DisallowExpr: regexp.MustCompile("disallow-.*"), | ||
| DisallowAll: true, | ||
| DisallowIsError: true, | ||
| }, | ||
| allowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("allow-header", false)}, | ||
| disallowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("disallow-header", false)}, | ||
| deferredCloseTimeout: 10 * time.Second, | ||
| }, | ||
| override: overrideConfig{ | ||
| failureModeAllow: optional.New(true), | ||
| requestAttributes: []string{"override-attr1"}, | ||
| responseAttributes: []string{"override-attr2"}, | ||
| processingModes: optional.New(processingModes{ | ||
| requestHeaderMode: modeSkip, | ||
| responseHeaderMode: modeSend, | ||
| responseTrailerMode: modeSkip, | ||
| requestBodyMode: modeSkip, | ||
| responseBodyMode: modeSend, | ||
| }), | ||
| server: optional.New(xdsresource.GRPCServiceConfig{ | ||
| TargetURI: "override-uri", | ||
| }), | ||
| }, | ||
| wantConfig: baseConfig{ | ||
| failureModeAllow: true, | ||
| requestAttributes: []string{"override-attr1"}, | ||
| responseAttributes: []string{"override-attr2"}, | ||
| mutationRules: httpfilter.HeaderMutationRules{ | ||
| AllowExpr: regexp.MustCompile("allow-.*"), | ||
| DisallowExpr: regexp.MustCompile("disallow-.*"), | ||
| DisallowAll: true, | ||
| DisallowIsError: true, | ||
| }, | ||
| observabilityMode: true, | ||
| disableImmediateResponse: true, | ||
| deferredCloseTimeout: 10 * time.Second, | ||
| processingModes: processingModes{ | ||
| requestHeaderMode: modeSkip, | ||
| responseHeaderMode: modeSend, | ||
| responseTrailerMode: modeSkip, | ||
| requestBodyMode: modeSkip, | ||
| responseBodyMode: modeSend, | ||
| }, | ||
| server: xdsresource.GRPCServiceConfig{ | ||
| TargetURI: "override-uri", | ||
| }, | ||
| allowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("allow-header", false)}, | ||
| disallowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("disallow-header", false)}, | ||
| }, | ||
| }, | ||
| { | ||
| name: "ConfigUsingBaseAndPartialOverride", | ||
| cfg: baseConfig{ | ||
| failureModeAllow: false, | ||
| requestAttributes: []string{"base-attr1"}, | ||
| responseAttributes: []string{"base-attr2"}, | ||
| observabilityMode: true, | ||
| disableImmediateResponse: true, | ||
| deferredCloseTimeout: 10 * time.Second, | ||
| processingModes: processingModes{ | ||
| requestHeaderMode: modeSend, | ||
| responseHeaderMode: modeSkip, | ||
| responseTrailerMode: modeSend, | ||
| requestBodyMode: modeSend, | ||
| responseBodyMode: modeSkip, | ||
| }, | ||
| server: xdsresource.GRPCServiceConfig{ | ||
| TargetURI: testBaseURI, | ||
| Timeout: time.Second, | ||
| InitialMetadata: metadata.MD(metadata.Pairs("key1", "value1")), | ||
| }, | ||
| mutationRules: httpfilter.HeaderMutationRules{ | ||
| AllowExpr: regexp.MustCompile("allow-.*"), | ||
| DisallowExpr: regexp.MustCompile("disallow-.*"), | ||
| DisallowAll: true, | ||
| DisallowIsError: true, | ||
| }, | ||
| allowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("allow-header", false)}, | ||
| disallowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("disallow-header", false)}, | ||
| }, | ||
| override: overrideConfig{ | ||
| failureModeAllow: optional.New(true), | ||
| }, | ||
| wantConfig: baseConfig{ | ||
| failureModeAllow: true, | ||
| requestAttributes: []string{"base-attr1"}, | ||
| responseAttributes: []string{"base-attr2"}, | ||
| mutationRules: httpfilter.HeaderMutationRules{ | ||
| AllowExpr: regexp.MustCompile("allow-.*"), | ||
| DisallowExpr: regexp.MustCompile("disallow-.*"), | ||
| DisallowAll: true, | ||
| DisallowIsError: true, | ||
| }, | ||
| observabilityMode: true, | ||
| disableImmediateResponse: true, | ||
| deferredCloseTimeout: 10 * time.Second, | ||
| processingModes: processingModes{ | ||
| requestHeaderMode: modeSend, | ||
| responseHeaderMode: modeSkip, | ||
| responseTrailerMode: modeSend, | ||
| requestBodyMode: modeSend, | ||
| responseBodyMode: modeSkip, | ||
| }, | ||
| server: xdsresource.GRPCServiceConfig{ | ||
| TargetURI: testBaseURI, | ||
| Timeout: time.Second, | ||
| InitialMetadata: metadata.MD(metadata.Pairs("key1", "value1")), | ||
| }, | ||
| allowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("allow-header", false)}, | ||
| disallowedHeaders: []matcher.StringMatcher{matcher.NewExactStringMatcher("disallow-header", false)}, | ||
| }, | ||
| }, | ||
| } | ||
| for _, tc := range tests { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| intptr, err := f.BuildClientInterceptor(tc.cfg, tc.override) | ||
| if tc.wantErr == "" { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this test logic is still reasonably complicated for a table driven test. Please consider splitting into success and failure cases.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| if err != nil { | ||
| t.Fatalf("BuildClientInterceptor() returned unexpected error: %v", err) | ||
| } | ||
| ic, ok := intptr.(*interceptor) | ||
| if !ok { | ||
| t.Fatalf("BuildClientInterceptor() returned %T, want *interceptor", intptr) | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this ever possible? Why not just do
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed |
||
| if diff := cmp.Diff(ic.config, tc.wantConfig, cmpOpts...); diff != "" { | ||
| t.Fatalf("Interceptor config returned unexpected diff (-got +want):\n%s", diff) | ||
| } | ||
| intptr.Close() | ||
| return | ||
| } | ||
| if err == nil { | ||
| t.Fatalf("BuildClientInterceptor() returned nil error, want error containing %q", tc.wantErr) | ||
| } | ||
| if !strings.Contains(err.Error(), tc.wantErr) { | ||
| t.Fatalf("BuildClientInterceptor() error = %v, want error containing %q", err, tc.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.