Skip to content

Commit a2753cf

Browse files
authored
apply plugin network filters to TCP filter chains (#10822)
Signed-off-by: Steven Landow <[email protected]>
1 parent 0c0c935 commit a2753cf

File tree

2 files changed

+131
-26
lines changed

2 files changed

+131
-26
lines changed

internal/kgateway/translator/irtranslator/fc.go

+13-26
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func (h *filterChainTranslator) initFilterChain(ctx context.Context, fcc ir.Filt
9494

9595
return fc
9696
}
97+
9798
func (h *filterChainTranslator) computeHttpFilters(ctx context.Context, l ir.HttpFilterChainIR, reporter reports.ListenerReporter) []*envoy_config_listener_v3.Filter {
9899
log := contextutils.LoggerFrom(ctx).Desugar()
99100

@@ -118,7 +119,7 @@ func (n *filterChainTranslator) computeNetworkFiltersForHttp(ctx context.Context
118119
reporter: reporter,
119120
gateway: n.gateway, // corresponds to Gateway API listener
120121
}
121-
networkFilters := sortNetworkFilters(n.computePreHCMFilters(ctx, l, reporter))
122+
networkFilters := sortNetworkFilters(n.computeCustomFilters(ctx, l.CustomNetworkFilters, reporter))
122123
networkFilter, err := hcm.computeNetworkFilters(ctx, l)
123124
if err != nil {
124125
return nil, err
@@ -127,7 +128,14 @@ func (n *filterChainTranslator) computeNetworkFiltersForHttp(ctx context.Context
127128
return networkFilters, nil
128129
}
129130

130-
func (n *filterChainTranslator) computePreHCMFilters(ctx context.Context, l ir.HttpFilterChainIR, reporter reports.ListenerReporter) []plugins.StagedNetworkFilter {
131+
// computeCustomFilters computes all custom filters, first from plugins, second
132+
// from embedded filters on the FilterChain itself.
133+
// For HTTP FilterChains these must be added before HCM.
134+
func (n *filterChainTranslator) computeCustomFilters(
135+
ctx context.Context,
136+
customNetworkFilters []ir.CustomEnvoyFilter,
137+
reporter reports.ListenerReporter,
138+
) []plugins.StagedNetworkFilter {
131139
var networkFilters []plugins.StagedNetworkFilter
132140
// Process the network filters.
133141
for _, plug := range n.PluginPass {
@@ -149,7 +157,7 @@ func (n *filterChainTranslator) computePreHCMFilters(ctx context.Context, l ir.H
149157
networkFilters = append(networkFilters, nf)
150158
}
151159
}
152-
networkFilters = append(networkFilters, convertCustomNetworkFilters(l.CustomNetworkFilters)...)
160+
networkFilters = append(networkFilters, convertCustomNetworkFilters(customNetworkFilters)...)
153161
return networkFilters
154162
}
155163

@@ -341,7 +349,7 @@ func sortHttpFilters(filters plugins.StagedHttpFilterList) []*envoyhttp.HttpFilt
341349
}
342350

343351
func (h *filterChainTranslator) computeTcpFilters(ctx context.Context, l ir.TcpIR, reporter reports.ListenerReporter) []*envoy_config_listener_v3.Filter {
344-
networkFilters := sortNetworkFilters(h.computeNetworkFiltersForTcp(l))
352+
networkFilters := sortNetworkFilters(h.computeCustomFilters(ctx, l.CustomNetworkFilters, reporter))
345353

346354
cfg := &envoytcp.TcpProxy{
347355
StatPrefix: l.FilterChainName,
@@ -372,28 +380,6 @@ func (h *filterChainTranslator) computeTcpFilters(ctx context.Context, l ir.TcpI
372380
return append(networkFilters, tcpFilter)
373381
}
374382

375-
func (t *filterChainTranslator) computeNetworkFiltersForTcp(l ir.TcpIR) []plugins.StagedNetworkFilter {
376-
var networkFilters []plugins.StagedNetworkFilter
377-
// Process the network filters.
378-
//for _, plug := range t.networkPlugins {
379-
// stagedFilters, err := plug.NetworkFiltersTCP(params, t.listener)
380-
// if err != nil {
381-
// validation.AppendTCPListenerError(t.report, validationapi.TcpListenerReport_Error_ProcessingError, err.Error())
382-
// }
383-
//
384-
// for _, nf := range stagedFilters {
385-
// if nf.Filter == nil {
386-
// log.Warnf("plugin %v implements NetworkFilters() but returned nil", plug.Name())
387-
// continue
388-
// }
389-
// networkFilters = append(networkFilters, nf)
390-
// }
391-
//}
392-
393-
networkFilters = append(networkFilters, convertCustomNetworkFilters(l.CustomNetworkFilters)...)
394-
return networkFilters
395-
}
396-
397383
func NewFilterWithTypedConfig(name string, config proto.Message) (*envoy_config_listener_v3.Filter, error) {
398384
s := &envoy_config_listener_v3.Filter{
399385
Name: name,
@@ -488,6 +474,7 @@ func (info *FilterChainInfo) toTransportSocket() *envoy_config_core_v3.Transport
488474
ConfigType: &envoy_config_core_v3.TransportSocket_TypedConfig{TypedConfig: typedConfig},
489475
}
490476
}
477+
491478
func bytesDataSource(s []byte) *envoy_config_core_v3.DataSource {
492479
return &envoy_config_core_v3.DataSource{
493480
Specifier: &envoy_config_core_v3.DataSource_InlineBytes{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package irtranslator_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
8+
9+
extensionsplug "github.com/kgateway-dev/kgateway/v2/internal/kgateway/extensions2/plugin"
10+
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/ir"
11+
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/plugins"
12+
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/reports"
13+
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/translator/irtranslator"
14+
15+
"istio.io/istio/pkg/ptr"
16+
"istio.io/istio/pkg/slices"
17+
18+
"k8s.io/apimachinery/pkg/runtime/schema"
19+
20+
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
21+
)
22+
23+
const (
24+
testPluginFilterName = "filter-from-plugin"
25+
testCustomFilterName = "filter-from-fc-field"
26+
)
27+
28+
var addFiltersGK = schema.GroupKind{
29+
Group: "test.kgateway.dev",
30+
Kind: "AddFilterForTest",
31+
}
32+
33+
// addFilters implements a test translation pass that adds network filters
34+
type addFilters struct {
35+
ir.UnimplementedProxyTranslationPass
36+
}
37+
38+
func (a addFilters) NetworkFilters(ctx context.Context) ([]plugins.StagedNetworkFilter, error) {
39+
return []plugins.StagedNetworkFilter{
40+
{
41+
Filter: &listenerv3.Filter{Name: testPluginFilterName},
42+
Stage: plugins.BeforeStage(plugins.AuthZStage),
43+
},
44+
}, nil
45+
}
46+
47+
func TestFilterChains(t *testing.T) {
48+
ctx := context.Background()
49+
50+
translator := irtranslator.Translator{
51+
// not used by the test today, but if we refactor to call newPass in the test
52+
// it will be necessary; leaving it here to save time debugging after a refactor
53+
ContributedPolicies: map[schema.GroupKind]extensionsplug.PolicyPlugin{
54+
addFiltersGK: {
55+
NewGatewayTranslationPass: func(ctx context.Context, tctx ir.GwTranslationCtx) ir.ProxyTranslationPass {
56+
return addFilters{}
57+
},
58+
},
59+
},
60+
}
61+
62+
// Create test gateway and listener IR
63+
gateway := ir.GatewayIR{SourceObject: &gwv1.Gateway{}}
64+
listener := ir.ListenerIR{
65+
HttpFilterChain: []ir.HttpFilterChainIR{{
66+
FilterChainCommon: ir.FilterChainCommon{
67+
FilterChainName: "httpchain",
68+
CustomNetworkFilters: []ir.CustomEnvoyFilter{{
69+
Name: testCustomFilterName,
70+
FilterStage: plugins.BeforeStage(plugins.AuthZStage),
71+
}},
72+
},
73+
}},
74+
TcpFilterChain: []ir.TcpIR{{
75+
FilterChainCommon: ir.FilterChainCommon{
76+
FilterChainName: "tcpchain",
77+
CustomNetworkFilters: []ir.CustomEnvoyFilter{{
78+
Name: testCustomFilterName,
79+
FilterStage: plugins.BeforeStage(plugins.AuthZStage),
80+
}},
81+
},
82+
}},
83+
}
84+
85+
// fake
86+
reportMap := reports.NewReportMap()
87+
reporter := reports.NewReporter(&reportMap)
88+
89+
// method under test
90+
envoyListener, _ := translator.ComputeListener(
91+
ctx,
92+
irtranslator.TranslationPassPlugins{
93+
addFiltersGK: &irtranslator.TranslationPass{ProxyTranslationPass: addFilters{}},
94+
},
95+
gateway,
96+
listener,
97+
reporter,
98+
)
99+
100+
expectedChainCount := len(listener.HttpFilterChain) + len(listener.TcpFilterChain)
101+
if len(envoyListener.FilterChains) != expectedChainCount {
102+
t.Fatal("got", len(envoyListener.FilterChains), "Envoy filter chains, but wanted", expectedChainCount)
103+
}
104+
105+
expectedFilters := []string{testPluginFilterName, testCustomFilterName}
106+
for _, filterChain := range envoyListener.FilterChains {
107+
for _, expectedFilterName := range expectedFilters {
108+
filter := ptr.Flatten(slices.FindFunc(filterChain.Filters, func(filter *listenerv3.Filter) bool {
109+
return filter.Name == expectedFilterName
110+
}))
111+
112+
if filter == nil {
113+
t.Errorf("filter chain %q missing expected filter %q",
114+
filterChain.Name, expectedFilterName)
115+
}
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)