Skip to content

Commit 005d80e

Browse files
cnvergencerudrakhpzirainaburanrbxarkodg
authored
[release/v1.7] cherry-pick for v1.7.2 (#8768)
* fix: handle network errors in rate limit e2e tests (#8446) Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com> (cherry picked from commit b0638d5) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * refactor/perf: use LuaPerRoute instead of FilterConfig (#8355) perf: use LuaPerRoute instead of FilterConfig Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com> (cherry picked from commit f31ac4e) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix: per-endpoint hostname override blocked by auto-generated wildcad host (#8565) * fix: per-endpoint hostname override blocked by auto-generated wildcard host Signed-off-by: zirain <zirain2009@gmail.com> * add UT Signed-off-by: zirain <zirain2009@gmail.com> --------- Signed-off-by: zirain <zirain2009@gmail.com> (cherry picked from commit 595010a) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix bug with grpcroute mirror filter (#8541) * fix bug with grpcroute mirror filter Signed-off-by: Adam Buran <aburan@roblox.com> * add indexers test Signed-off-by: Adam Buran <aburan@roblox.com> * add release note Signed-off-by: Adam Buran <aburan@roblox.com> --------- Signed-off-by: Adam Buran <aburan@roblox.com> Signed-off-by: Arko Dasgupta <arkodg@users.noreply.github.com> Co-authored-by: Arko Dasgupta <arkodg@users.noreply.github.com> (cherry picked from commit e633c08) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix: normalize CRLF line endings in htpasswd basic auth secrets (#8557) Fixes #8554 Signed-off-by: stekole <stefan@sandnetworks.com> Signed-off-by: stekole <30674956+stekole@users.noreply.github.com> (cherry picked from commit 9cac348) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix: avoid metric increments on no-op delete reconcile paths (#8480) * fix: avoid metric increments on no-op delete reconcile paths Signed-off-by: Felipe Sabadini Facina <fsabadini@hotmail.com> * Update internal/infrastructure/kubernetes/infra_resource_test.go Signed-off-by: Isaac Wilson <isaac.wilson514@gmail.com> * Update internal/infrastructure/kubernetes/infra_resource_test.go Signed-off-by: Isaac Wilson <isaac.wilson514@gmail.com> --------- Signed-off-by: Felipe Sabadini Facina <fsabadini@hotmail.com> Signed-off-by: Isaac Wilson <isaac.wilson514@gmail.com> Co-authored-by: Isaac Wilson <isaac.wilson514@gmail.com> (cherry picked from commit 7a2a4ec) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix(telemetry): support BackendTLSPolicy for telemetry backends (#8545) * fix(telemetry): support BackendTLSPolicy for telemetry backends processBackendRefs does not look up BackendTLSPolicy for telemetry backends (access logs, tracing, metrics), so TLS can only be configured via Backend.spec.tls. Replace inline processServerValidationTLSSettings with applyBackendTLSSetting so telemetry backends get the full Backend + BackendTLSPolicy + EnvoyProxy TLS merge. Workaround: envoyproxy/ai-gateway#1964 Signed-off-by: Adrian Cole <adrian@tetrate.io> (cherry picked from commit ac18feb) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix: restore failure-path metric recording for delete and HPA reconcile (#8656) Fixes #8651 Signed-off-by: Felipe Sabadini Facina <fsabadini@hotmail.com> (cherry picked from commit 2a5bfd0) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix: status for mirror backend (#8675) Signed-off-by: kkk777-7 <kota.kimura0725@gmail.com> (cherry picked from commit fa81778) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix: client certificate secret never delivered when it is exclusively referenced by a SecurityPolicy extAuth Backend (#8654) * fix: client certificate secret never delivered when it is exclusively referenced by a SecurityPolicy extAuth Backend Signed-off-by: zirain <zirain2009@gmail.com> * fix lint Signed-off-by: zirain <zirain2009@gmail.com> --------- Signed-off-by: zirain <zirain2009@gmail.com> (cherry picked from commit c7e21fa) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix: client certificate secret never delivered when it is exclusively referenced by a SecurityPolicy jwt/oidc Backend (#8711) Signed-off-by: zirain <zirain2009@gmail.com> (cherry picked from commit 95c3a79) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix: helm secrets rbac for gateway namespace with watch list of namespaces (#8706) * fix: helm secrets rbac for gateway namespace with watch list of namespaces Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * add release notes Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * review update Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> --------- Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> Co-authored-by: Rudrakh Panigrahi <rudrakh97@gmail.com> (cherry picked from commit c48a346) Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * add release notes Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix lint Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> * fix lint Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> --------- Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com> Signed-off-by: Karol Szwaj <karol.szwaj@gmail.com> Signed-off-by: zirain <zirain2009@gmail.com> Signed-off-by: Adam Buran <aburan@roblox.com> Signed-off-by: Arko Dasgupta <arkodg@users.noreply.github.com> Signed-off-by: stekole <stefan@sandnetworks.com> Signed-off-by: stekole <30674956+stekole@users.noreply.github.com> Signed-off-by: Felipe Sabadini Facina <fsabadini@hotmail.com> Signed-off-by: Isaac Wilson <isaac.wilson514@gmail.com> Signed-off-by: Adrian Cole <adrian@tetrate.io> Signed-off-by: kkk777-7 <kota.kimura0725@gmail.com> Co-authored-by: Rudrakh Panigrahi <rudrakh97@gmail.com> Co-authored-by: zirain <zirain2009@gmail.com> Co-authored-by: aburanrbx <aburan@roblox.com> Co-authored-by: Arko Dasgupta <arkodg@users.noreply.github.com> Co-authored-by: stekole <30674956+stekole@users.noreply.github.com> Co-authored-by: Felipe Sabadini <fsabadini@hotmail.com> Co-authored-by: Isaac Wilson <isaac.wilson514@gmail.com> Co-authored-by: Adrian Cole <64215+codefromthecrypt@users.noreply.github.com> Co-authored-by: Kota Kimura <86363983+kkk777-7@users.noreply.github.com>
1 parent 09d3981 commit 005d80e

51 files changed

Lines changed: 2724 additions & 249 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

charts/gateway-helm/templates/_rbac.tpl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,17 @@ verbs:
263263
- watch
264264
{{- end }}
265265

266+
{{- define "eg.rbac.controllernamespace.secrets.read" -}}
267+
- apiGroups:
268+
- ""
269+
resources:
270+
- secrets
271+
verbs:
272+
- get
273+
- list
274+
- watch
275+
{{- end }}
276+
266277
{{- define "eg.rbac.infra.tokenreview" -}}
267278
- apiGroups:
268279
- authentication.k8s.io

charts/gateway-helm/templates/infra-manager-rbac.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ metadata:
4242
{{- include "eg.labels" . | nindent 4 }}
4343
rules:
4444
{{ include "eg.rbac.infra.basic" . }}
45+
{{ if and (.Values.config.envoyGateway.provider.kubernetes) (.Values.config.envoyGateway.provider.kubernetes.watch) (.Values.config.envoyGateway.provider.kubernetes.deploy) (eq .Values.config.envoyGateway.provider.kubernetes.deploy.type "GatewayNamespace") (.Values.config.envoyGateway.provider.kubernetes.watch.namespaces) (gt (len .Values.config.envoyGateway.provider.kubernetes.watch.namespaces) 0) }}
46+
{{ include "eg.rbac.controllernamespace.secrets.read" . }}
47+
{{ end }}
4548
---
4649
apiVersion: rbac.authorization.k8s.io/v1
4750
kind: RoleBinding

internal/gatewayapi/filters.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,16 @@ func (t *Translator) ProcessGRPCFilters(
209209
}
210210
}
211211

212+
if httpFiltersContext.DirectResponse != nil && len(httpFiltersContext.Mirrors) > 0 {
213+
httpFiltersContext.DirectResponse = nil
214+
httpFiltersContext.Mirrors = nil
215+
216+
errs.Add(status.NewRouteStatusError(
217+
errors.New(requestMirrorDirectResponseConflictMsg),
218+
gwapiv1.RouteReasonIncompatibleFilters,
219+
).WithType(gwapiv1.RouteConditionAccepted))
220+
}
221+
212222
return httpFiltersContext, errs.GetAllErrors()
213223
}
214224

@@ -1042,6 +1052,13 @@ func (t *Translator) processRequestMirrorFilter(
10421052
settingName := irDestinationSettingName(destName, -1 /*unused*/)
10431053
ds, _, err := t.processDestination(settingName, mirrorBackendRef, filterContext.ParentRef, filterContext.Route, resources)
10441054
if err != nil {
1055+
// Gateway API conformance: When backendRef Service exists but has no endpoints,
1056+
// the ResolvedRefs condition should NOT be set to False.
1057+
// so we return the custom condition error to handle this case and set the status in the caller function.
1058+
if err.Reason() == status.RouteReasonEndpointsNotFound {
1059+
return status.NewRouteStatusError(
1060+
fmt.Errorf("failed to validate the RequestMirror filter: %w", err), err.Reason()).WithType(status.RouteConditionBackendsAvailable)
1061+
}
10451062
return err
10461063
}
10471064

internal/gatewayapi/listener.go

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources *
634634
destName := fmt.Sprintf("accesslog_als_%d_%d", i, j)
635635
settingName := irDestinationSettingName(destName, -1)
636636
// TODO: how to get authority from the backendRefs?
637-
ds, traffic, err := t.processBackendRefs(settingName, sink.ALS.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
637+
ds, traffic, err := t.processBackendRefsForTelemetry(settingName, sink.ALS.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
638638
if err != nil {
639639
return nil, err
640640
}
@@ -680,7 +680,7 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources *
680680
// TODO: rename this, so that we can share backend with tracing?
681681
destName := fmt.Sprintf("accesslog_otel_%d_%d", i, j)
682682
settingName := irDestinationSettingName(destName, -1)
683-
ds, traffic, err := t.processBackendRefs(settingName, sink.OpenTelemetry.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
683+
ds, traffic, err := t.processBackendRefsForTelemetry(settingName, sink.OpenTelemetry.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
684684
if err != nil {
685685
return nil, err
686686
}
@@ -743,7 +743,7 @@ func (t *Translator) processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.Envo
743743
// TODO: rename this, so that we can share backend with accesslog?
744744
destName := "tracing"
745745
settingName := irDestinationSettingName(destName, -1)
746-
ds, traffic, err := t.processBackendRefs(settingName, tracing.Provider.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
746+
ds, traffic, err := t.processBackendRefsForTelemetry(settingName, tracing.Provider.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
747747
if err != nil {
748748
return nil, err
749749
}
@@ -883,7 +883,7 @@ func (t *Translator) processMetrics(envoyproxy *egv1a1.EnvoyProxy, resources *re
883883

884884
destName := fmt.Sprintf("metrics_otel_%d", i)
885885
settingName := irDestinationSettingName(destName, -1)
886-
ds, _, err := t.processBackendRefs(settingName, sink.OpenTelemetry.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
886+
ds, _, err := t.processBackendRefsForTelemetry(settingName, sink.OpenTelemetry.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
887887
if err != nil {
888888
return nil, nil, err
889889
}
@@ -931,54 +931,66 @@ func (t *Translator) processMetrics(envoyproxy *egv1a1.EnvoyProxy, resources *re
931931
}, resolvedSinks, nil
932932
}
933933

934-
func (t *Translator) processBackendRefs(name string, backendCluster egv1a1.BackendCluster, namespace string,
934+
func (t *Translator) processBackendRefsForTelemetry(name string, backendCluster egv1a1.BackendCluster, namespace string,
935935
resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy,
936936
) ([]*ir.DestinationSetting, *ir.TrafficFeatures, error) {
937937
traffic, err := translateTrafficFeatures(backendCluster.BackendSettings)
938938
if err != nil {
939939
return nil, nil, err
940940
}
941+
942+
parent := gwapiv1.ParentReference{
943+
Group: ptr.To(gwapiv1.Group(egv1a1.GroupName)),
944+
Kind: ptr.To(gwapiv1.Kind(egv1a1.KindEnvoyProxy)),
945+
Namespace: ptr.To(gwapiv1.Namespace(envoyProxy.Namespace)),
946+
Name: gwapiv1.ObjectName(envoyProxy.Name),
947+
}
948+
941949
result := make([]*ir.DestinationSetting, 0, len(backendCluster.BackendRefs))
942950
for i := range backendCluster.BackendRefs {
943951
ref := &backendCluster.BackendRefs[i]
944952
ns := NamespaceDerefOr(ref.Namespace, namespace)
945953
kind := KindDerefOr(ref.Kind, resource.KindService)
954+
955+
var ds *ir.DestinationSetting
946956
switch kind {
947957
case resource.KindService:
948958
if err := t.validateBackendRefService(ref.BackendObjectReference, ns, corev1.ProtocolTCP); err != nil {
949959
return nil, nil, err
950960
}
951-
ds, err := t.processServiceDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, envoyProxy)
961+
ds, err = t.processServiceDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, envoyProxy)
952962
if err != nil {
953963
return nil, nil, err
954964
}
955-
result = append(result, ds)
956965
case resource.KindBackend:
957966
if err := t.validateBackendRefBackend(ref.BackendObjectReference, resources, ns, true); err != nil {
958967
return nil, nil, err
959968
}
960-
ds := t.processBackendDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP)
969+
ds = t.processBackendDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP)
961970
// Dynamic resolver destinations are not supported for none-route destinations
962971
if ds.IsDynamicResolver {
963972
return nil, nil, errors.New("dynamic resolver destinations are not supported")
964973
}
965-
// Apply TLS config for backend (telemetry) clusters
966-
backend := t.GetBackend(ns, string(ref.Name))
967-
if backend.Spec.TLS != nil {
968-
tlsConfig, err := t.processServerValidationTLSSettings(backend)
969-
if err != nil {
970-
return nil, nil, err
971-
}
972-
ds.TLS = tlsConfig
973-
// Infer SNI from FQDN for telemetry backends (no Host header available)
974-
if ds.TLS.SNI == nil && len(backend.Spec.Endpoints) == 1 && backend.Spec.Endpoints[0].FQDN != nil {
975-
ds.TLS.SNI = &backend.Spec.Endpoints[0].FQDN.Hostname
976-
}
977-
}
978-
result = append(result, ds)
979974
default:
980975
return nil, nil, fmt.Errorf("unsupported kind for backendRefs: %s", kind)
981976
}
977+
978+
// Apply TLS from Backend resource, BackendTLSPolicy, and EnvoyProxy.
979+
backendTLS, err := t.applyBackendTLSSetting(ref.BackendObjectReference, ns, parent, resources, envoyProxy)
980+
if err != nil {
981+
return nil, nil, err
982+
}
983+
ds.TLS = backendTLS
984+
985+
// Infer SNI from FQDN for telemetry backends (no Host header available).
986+
if ds.TLS != nil && ds.TLS.SNI == nil && kind == resource.KindBackend {
987+
backend := t.GetBackend(ns, string(ref.Name))
988+
if len(backend.Spec.Endpoints) == 1 && backend.Spec.Endpoints[0].FQDN != nil {
989+
ds.TLS.SNI = &backend.Spec.Endpoints[0].FQDN.Hostname
990+
}
991+
}
992+
993+
result = append(result, ds)
982994
}
983995
if len(result) == 0 {
984996
return nil, traffic, nil

internal/gatewayapi/listener_test.go

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,7 @@ func TestProcessBackendRefsSNIInference(t *testing.T) {
12881288
}},
12891289
}
12901290

1291-
ds, _, err := translator.processBackendRefs("test", backendCluster, ns, resources, nil)
1291+
ds, _, err := translator.processBackendRefsForTelemetry("test", backendCluster, ns, resources, &egv1a1.EnvoyProxy{ObjectMeta: metav1.ObjectMeta{Namespace: "envoy-gateway-system", Name: "test-proxy"}})
12921292
require.NoError(t, err)
12931293
require.Len(t, ds, 1)
12941294

@@ -1303,3 +1303,176 @@ func TestProcessBackendRefsSNIInference(t *testing.T) {
13031303
})
13041304
}
13051305
}
1306+
1307+
func TestProcessBackendRefsBackendTLSPolicy(t *testing.T) {
1308+
ns := "test-ns"
1309+
backendName := "otel-collector"
1310+
serviceName := "otel-svc"
1311+
envoyProxy := &egv1a1.EnvoyProxy{ObjectMeta: metav1.ObjectMeta{Namespace: "envoy-gateway-system", Name: "test-proxy"}}
1312+
1313+
backendBackendCluster := egv1a1.BackendCluster{BackendRefs: []egv1a1.BackendRef{{
1314+
BackendObjectReference: gwapiv1.BackendObjectReference{
1315+
Group: ptr.To(gwapiv1.Group("gateway.envoyproxy.io")), Kind: ptr.To(gwapiv1.Kind("Backend")),
1316+
Name: gwapiv1.ObjectName(backendName), Namespace: ptr.To(gwapiv1.Namespace(ns)),
1317+
},
1318+
}}}
1319+
otelBackend := &egv1a1.Backend{
1320+
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: backendName},
1321+
Spec: egv1a1.BackendSpec{Endpoints: []egv1a1.BackendEndpoint{{FQDN: &egv1a1.FQDNEndpoint{Hostname: "otel.example.com", Port: 443}}}},
1322+
}
1323+
otelBackendWithTLS := &egv1a1.Backend{
1324+
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: backendName},
1325+
Spec: egv1a1.BackendSpec{
1326+
Endpoints: []egv1a1.BackendEndpoint{{FQDN: &egv1a1.FQDNEndpoint{Hostname: "otel.example.com", Port: 443}}},
1327+
TLS: &egv1a1.BackendTLSSettings{
1328+
WellKnownCACertificates: ptr.To(gwapiv1.WellKnownCACertificatesSystem),
1329+
SNI: ptr.To(gwapiv1.PreciseHostname("backend-sni.example.com")),
1330+
},
1331+
},
1332+
}
1333+
otelBackendPolicy := &gwapiv1.BackendTLSPolicy{
1334+
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: "otel-tls"},
1335+
Spec: gwapiv1.BackendTLSPolicySpec{
1336+
TargetRefs: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{{
1337+
LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
1338+
Group: "gateway.envoyproxy.io", Kind: "Backend", Name: gwapiv1.ObjectName(backendName),
1339+
},
1340+
}},
1341+
Validation: gwapiv1.BackendTLSPolicyValidation{
1342+
WellKnownCACertificates: ptr.To(gwapiv1.WellKnownCACertificatesSystem),
1343+
Hostname: "otel.example.com",
1344+
},
1345+
},
1346+
}
1347+
backendEndpoints := []*ir.DestinationEndpoint{{Host: "otel.example.com", Port: 443}}
1348+
backendMetadata := &ir.ResourceMetadata{Name: backendName, Namespace: ns}
1349+
backendPolicyTLS := &ir.TLSUpstreamConfig{
1350+
SNI: ptr.To("otel.example.com"), UseSystemTrustStore: true,
1351+
CACertificate: &ir.TLSCACertificate{Name: "otel-tls/test-ns-ca"}, SubjectAltNames: []ir.SubjectAltName{},
1352+
}
1353+
1354+
serviceBackendCluster := egv1a1.BackendCluster{BackendRefs: []egv1a1.BackendRef{{
1355+
BackendObjectReference: gwapiv1.BackendObjectReference{
1356+
Name: gwapiv1.ObjectName(serviceName), Namespace: ptr.To(gwapiv1.Namespace(ns)),
1357+
Port: ptr.To(gwapiv1.PortNumber(4317)),
1358+
},
1359+
}}}
1360+
otelService := &corev1.Service{
1361+
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: serviceName},
1362+
Spec: corev1.ServiceSpec{ClusterIP: "7.7.7.7", Ports: []corev1.ServicePort{{
1363+
Name: "grpc", Port: 4317, TargetPort: intstr.IntOrString{IntVal: 4317}, Protocol: corev1.ProtocolTCP,
1364+
}}},
1365+
}
1366+
otelEndpointSlice := &discoveryv1.EndpointSlice{
1367+
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: serviceName, Labels: map[string]string{"kubernetes.io/service-name": serviceName}},
1368+
AddressType: discoveryv1.AddressTypeIPv4,
1369+
Endpoints: []discoveryv1.Endpoint{{Addresses: []string{"7.7.7.7"}, Conditions: discoveryv1.EndpointConditions{Ready: ptr.To(true)}}},
1370+
Ports: []discoveryv1.EndpointPort{{Name: ptr.To("grpc"), Port: ptr.To(int32(4317)), Protocol: ptr.To(corev1.ProtocolTCP)}},
1371+
}
1372+
otelServicePolicy := &gwapiv1.BackendTLSPolicy{
1373+
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: "otel-svc-tls"},
1374+
Spec: gwapiv1.BackendTLSPolicySpec{
1375+
TargetRefs: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{{
1376+
LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{Kind: "Service", Name: gwapiv1.ObjectName(serviceName)},
1377+
SectionName: ptr.To(gwapiv1.SectionName("grpc")),
1378+
}},
1379+
Validation: gwapiv1.BackendTLSPolicyValidation{
1380+
WellKnownCACertificates: ptr.To(gwapiv1.WellKnownCACertificatesSystem),
1381+
Hostname: "otel-svc.example.com",
1382+
},
1383+
},
1384+
}
1385+
serviceEndpoints := []*ir.DestinationEndpoint{{Host: "7.7.7.7", Port: 4317}}
1386+
serviceMetadata := &ir.ResourceMetadata{Name: serviceName, Namespace: ns, SectionName: "4317"}
1387+
servicePolicyTLS := &ir.TLSUpstreamConfig{
1388+
SNI: ptr.To("otel-svc.example.com"), UseSystemTrustStore: true,
1389+
CACertificate: &ir.TLSCACertificate{Name: "otel-svc-tls/test-ns-ca"}, SubjectAltNames: []ir.SubjectAltName{},
1390+
}
1391+
1392+
tests := []struct {
1393+
name string
1394+
backendCluster egv1a1.BackendCluster
1395+
context *TranslatorContext
1396+
resources *resource.Resources
1397+
expected []*ir.DestinationSetting
1398+
expectedErr string
1399+
}{
1400+
{
1401+
name: "BackendTLSPolicy without Backend TLS",
1402+
backendCluster: backendBackendCluster,
1403+
context: &TranslatorContext{BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: ns, Name: backendName}: otelBackend}},
1404+
resources: &resource.Resources{Backends: []*egv1a1.Backend{otelBackend}, BackendTLSPolicies: []*gwapiv1.BackendTLSPolicy{otelBackendPolicy}},
1405+
expected: []*ir.DestinationSetting{{
1406+
Name: "test", Protocol: ir.TCP, Endpoints: backendEndpoints,
1407+
AddressType: ptr.To(ir.FQDN), Metadata: backendMetadata, TLS: backendPolicyTLS,
1408+
}},
1409+
},
1410+
{
1411+
name: "no BackendTLSPolicy and no Backend TLS",
1412+
backendCluster: backendBackendCluster,
1413+
context: &TranslatorContext{BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: ns, Name: backendName}: otelBackend}},
1414+
resources: &resource.Resources{Backends: []*egv1a1.Backend{otelBackend}},
1415+
expected: []*ir.DestinationSetting{{
1416+
Name: "test", Protocol: ir.TCP, Endpoints: backendEndpoints,
1417+
AddressType: ptr.To(ir.FQDN), Metadata: backendMetadata,
1418+
}},
1419+
},
1420+
{
1421+
name: "Backend ref without namespace, no TLS, no BackendTLSPolicy",
1422+
backendCluster: egv1a1.BackendCluster{BackendRefs: []egv1a1.BackendRef{{
1423+
BackendObjectReference: gwapiv1.BackendObjectReference{
1424+
Group: ptr.To(gwapiv1.Group("gateway.envoyproxy.io")), Kind: ptr.To(gwapiv1.Kind("Backend")),
1425+
Name: gwapiv1.ObjectName(backendName),
1426+
},
1427+
}}},
1428+
context: &TranslatorContext{BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: ns, Name: backendName}: otelBackend}},
1429+
resources: &resource.Resources{Backends: []*egv1a1.Backend{otelBackend}},
1430+
expected: []*ir.DestinationSetting{{
1431+
Name: "test", Protocol: ir.TCP, Endpoints: backendEndpoints,
1432+
AddressType: ptr.To(ir.FQDN), Metadata: backendMetadata,
1433+
}},
1434+
},
1435+
{
1436+
name: "BackendTLSPolicy overrides Backend TLS SNI",
1437+
backendCluster: backendBackendCluster,
1438+
context: &TranslatorContext{BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: ns, Name: backendName}: otelBackendWithTLS}},
1439+
resources: &resource.Resources{Backends: []*egv1a1.Backend{otelBackendWithTLS}, BackendTLSPolicies: []*gwapiv1.BackendTLSPolicy{otelBackendPolicy}},
1440+
expected: []*ir.DestinationSetting{{
1441+
Name: "test", Protocol: ir.TCP, Endpoints: backendEndpoints,
1442+
AddressType: ptr.To(ir.FQDN), Metadata: backendMetadata, TLS: backendPolicyTLS,
1443+
}},
1444+
},
1445+
{
1446+
name: "BackendTLSPolicy for Service",
1447+
backendCluster: serviceBackendCluster,
1448+
context: &TranslatorContext{
1449+
ServiceMap: map[types.NamespacedName]*corev1.Service{{Namespace: ns, Name: serviceName}: otelService},
1450+
EndpointSliceMap: map[backendServiceKey][]*discoveryv1.EndpointSlice{
1451+
{kind: resource.KindService, namespace: ns, name: serviceName}: {otelEndpointSlice},
1452+
},
1453+
},
1454+
resources: &resource.Resources{BackendTLSPolicies: []*gwapiv1.BackendTLSPolicy{otelServicePolicy}},
1455+
expected: []*ir.DestinationSetting{{
1456+
Name: "test", Protocol: ir.TCP, Endpoints: serviceEndpoints,
1457+
AddressType: ptr.To(ir.IP), Metadata: serviceMetadata, TLS: servicePolicyTLS,
1458+
}},
1459+
},
1460+
}
1461+
1462+
for _, tc := range tests {
1463+
t.Run(tc.name, func(t *testing.T) {
1464+
translator := &Translator{
1465+
TranslatorContext: tc.context,
1466+
BackendEnabled: true,
1467+
GatewayControllerName: egv1a1.GatewayControllerName,
1468+
}
1469+
ds, _, err := translator.processBackendRefsForTelemetry("test", tc.backendCluster, ns, tc.resources, envoyProxy)
1470+
if tc.expectedErr != "" {
1471+
require.EqualError(t, err, tc.expectedErr)
1472+
} else {
1473+
require.NoError(t, err)
1474+
require.Equal(t, tc.expected, ds)
1475+
}
1476+
})
1477+
}
1478+
}

internal/gatewayapi/route.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,12 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe
235235
if len(errs) > 0 {
236236
for _, err := range errs {
237237
errorCollector.Add(err)
238-
processFilterError = errors.Join(processFilterError, err)
238+
// Gateway API conformance: When a filter's backend Service exists but has no
239+
// endpoints (e.g. RequestMirror), do not treat it as a fatal filter error.
240+
// The route should continue to work; only BackendsAvailable is set to False.
241+
if err.Type() != status.RouteConditionBackendsAvailable {
242+
processFilterError = errors.Join(processFilterError, err)
243+
}
239244
if err.Type() == gwapiv1.RouteConditionAccepted {
240245
unacceptedRules.Insert(ruleIdx)
241246
}
@@ -958,7 +963,12 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe
958963
if len(errs) > 0 {
959964
for _, err := range errs {
960965
errorCollector.Add(err)
961-
processFilterError = errors.Join(processFilterError, err)
966+
// Gateway API conformance: When a filter's backend Service exists but has no
967+
// endpoints (e.g. RequestMirror), do not treat it as a fatal filter error.
968+
// The route should continue to work; only BackendsAvailable is set to False.
969+
if err.Type() != status.RouteConditionBackendsAvailable {
970+
processFilterError = errors.Join(processFilterError, err)
971+
}
962972
if err.Type() == gwapiv1.RouteConditionAccepted {
963973
unacceptedRules.Insert(ruleIdx)
964974
}

0 commit comments

Comments
 (0)