Skip to content

Commit d5cbd23

Browse files
committed
gatewayapi,ir,xds/translator: naming and comment cleanup from code review
- Rename btpClusterSettingsHasSettings to btpSpecHasClusterScopedFields (and its test) - the old name read as testing "ClusterSettings" the struct, when it actually checks the whole BackendTrafficPolicySpec for any CDS-scoped field. - Trim ir.BackendCluster.Merged's doc comment to a one-liner, consistent with the struct's other fields. - Rewrite singleResolvedClusterName's comment: frame the registry-lookup indirection as xds/translator defensively protecting itself against not-necessarily-well-formed data from an earlier layer, rather than tracing specific (and driftable) legit code paths that could produce a ref-without-registration. - Rename mergeIncompatibleForRule/mergeIncompatibleForNonWeightedRule to mergeIncompatibleForWeightedRule/mergeIncompatibleForSingleClusterRule - the old names read as a general case and a negated special case of it, when they're actually independent implementations for two route-type families distinguished by whether their route action supports weighted clusters. Split mergeIncompatibleForSingleClusterRule's combined boolean into guard clauses with a comment per condition, matching the sibling function's established style. - mergeIncompatibleForWeightedRule: add a comment to the len(backendRefs) <= 1 guard, and restructure the final ConsistentHash condition into an explicit if/return true with a trailing return false, for consistency with the other guard clauses. Its position (checked before session persistence, fallback backend, and ConsistentHash) is intentional and unchanged: those three checks protect a multi-backend pool from fragmenting, which doesn't apply when there's nothing to fragment. Signed-off-by: Muhammad Waqar <waqar.hameed08@gmail.com>
1 parent 5749bc8 commit d5cbd23

6 files changed

Lines changed: 47 additions & 36 deletions

File tree

internal/gatewayapi/backendtrafficpolicy.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,10 @@ func (idx *BTPRoutingTypeIndex) LookupGatewayBTRoutingType(gatewayNN types.Names
211211
return nil
212212
}
213213

214-
// btpClusterSettingsHasSettings reports whether spec sets any backend-cluster-scoped (CDS) field —
214+
// btpSpecHasClusterScopedFields reports whether spec sets any backend-cluster-scoped (CDS) field —
215215
// either directly inside its embedded ClusterSettings, or via a sibling field on the spec that also
216216
// affects the generated Cluster resource (UseClientProtocol feeds into HTTPRouteTranslator.asClusterArgs).
217-
func btpClusterSettingsHasSettings(spec *egv1a1.BackendTrafficPolicySpec) bool {
217+
func btpSpecHasClusterScopedFields(spec *egv1a1.BackendTrafficPolicySpec) bool {
218218
if spec == nil {
219219
return false
220220
}
@@ -232,7 +232,7 @@ func btpClusterSettingsHasSettings(spec *egv1a1.BackendTrafficPolicySpec) bool {
232232

233233
func hasBTPClusterSettings(btps []*egv1a1.BackendTrafficPolicy) bool {
234234
for _, btp := range btps {
235-
if btpClusterSettingsHasSettings(&btp.Spec) {
235+
if btpSpecHasClusterScopedFields(&btp.Spec) {
236236
return true
237237
}
238238
}
@@ -269,7 +269,7 @@ func BuildBTPClusterSettingsIndex(
269269
}
270270

271271
for _, btp := range btps {
272-
if !btpClusterSettingsHasSettings(&btp.Spec) {
272+
if !btpSpecHasClusterScopedFields(&btp.Spec) {
273273
continue
274274
}
275275
refs := resolvePolicyTargets(

internal/gatewayapi/backendtrafficpolicy_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,7 +1988,7 @@ func TestBTPLoadBalancerIndexIsConsistentHash(t *testing.T) {
19881988
}
19891989
}
19901990

1991-
func TestBtpClusterSettingsHasSettings(t *testing.T) {
1991+
func TestBtpSpecHasClusterScopedFields(t *testing.T) {
19921992
circuitBreakerSet := &egv1a1.ClusterSettings{CircuitBreaker: &egv1a1.CircuitBreaker{}}
19931993
useClientProtocolTrue := true
19941994

@@ -2020,7 +2020,7 @@ func TestBtpClusterSettingsHasSettings(t *testing.T) {
20202020
}
20212021
for _, tc := range tests {
20222022
t.Run(tc.name, func(t *testing.T) {
2023-
require.Equal(t, tc.want, btpClusterSettingsHasSettings(tc.spec))
2023+
require.Equal(t, tc.want, btpSpecHasClusterScopedFields(tc.spec))
20242024
})
20252025
}
20262026
}

internal/gatewayapi/route.go

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe
280280
for i := range rule.BackendRefs {
281281
backendRefs[i] = rule.BackendRefs[i].BackendObjectReference
282282
}
283-
mergeIncompatible := t.mergeIncompatibleForRule(httpRoute, parentRef, rule.Name, backendRefs, hasRouteLevelClusterSettings, rule.SessionPersistence != nil, gatewayCtx)
283+
mergeIncompatible := t.mergeIncompatibleForWeightedRule(httpRoute, parentRef, rule.Name, backendRefs, hasRouteLevelClusterSettings, rule.SessionPersistence != nil, gatewayCtx)
284284

285285
for i := range rule.BackendRefs {
286286
backendNamespace := NamespaceDerefOr(rule.BackendRefs[i].Namespace, httpRoute.GetNamespace())
@@ -698,10 +698,11 @@ func (t *Translator) isFallbackBackend(backendRef gwapiv1.BackendObjectReference
698698
return backend != nil && ptr.Deref(backend.Spec.Fallback, false)
699699
}
700700

701-
// mergeIncompatibleForRule reports whether a rule-level condition makes cluster deduplication
702-
// unsafe for any of this rule's backendRefs: route-level cluster settings, session persistence,
703-
// a fallback backend, or ConsistentHash load balancing.
704-
func (t *Translator) mergeIncompatibleForRule(
701+
// mergeIncompatibleForWeightedRule reports whether a rule-level condition makes cluster
702+
// deduplication unsafe for any of this rule's backendRefs: route-level cluster settings, session
703+
// persistence, a fallback backend, or ConsistentHash load balancing. For HTTP/GRPC, whose
704+
// weighted-clusters route action can represent multiple distinct clusters in one rule.
705+
func (t *Translator) mergeIncompatibleForWeightedRule(
705706
route RouteContext,
706707
parentRef *RouteParentContext,
707708
ruleName *gwapiv1.SectionName,
@@ -715,6 +716,8 @@ func (t *Translator) mergeIncompatibleForRule(
715716
if hasRouteLevelClusterSettings {
716717
return true
717718
}
719+
// A single backendRef has no multi-backend pool for the checks below to protect —
720+
// nothing to fragment, so it's always mergeable at this point.
718721
if len(backendRefs) <= 1 {
719722
return false
720723
}
@@ -729,16 +732,29 @@ func (t *Translator) mergeIncompatibleForRule(
729732
}
730733
}
731734
// ConsistentHash needs the full combined backend pool, not per-identity split clusters.
732-
return gatewayCtx != nil && t.BTPLoadBalancerIndex.IsConsistentHash(route.GetRouteType(), utils.NamespacedName(route), utils.NamespacedName(gatewayCtx.Gateway), parentRef.SectionName, ruleName)
735+
if gatewayCtx != nil && t.BTPLoadBalancerIndex.IsConsistentHash(route.GetRouteType(), utils.NamespacedName(route), utils.NamespacedName(gatewayCtx.Gateway), parentRef.SectionName, ruleName) {
736+
return true
737+
}
738+
return false
733739
}
734740

735-
// mergeIncompatibleForNonWeightedRule reports whether hasRouteLevelClusterSettings, or the rule
736-
// having more than one backendRef, makes cluster deduplication unsafe for a TCP/UDP/TLS rule.
737-
// Unlike HTTP/GRPC, these route types have no weighted-cluster mechanism at the listener layer,
738-
// so a rule's backendRefs must always land in a single cluster; letting them merge independently
739-
// would silently split the rule across clusters the listener can't reference together.
740-
func mergeIncompatibleForNonWeightedRule(hasRouteLevelClusterSettings bool, backendRefCount int) bool {
741-
return hasRouteLevelClusterSettings || backendRefCount > 1
741+
// mergeIncompatibleForSingleClusterRule reports whether a rule-level condition makes cluster
742+
// deduplication unsafe for a TCP/UDP/TLS rule. Unlike HTTP/GRPC, these route types have no
743+
// weighted-cluster mechanism at the listener layer, so a rule's backendRefs must always resolve
744+
// to a single cluster.
745+
func mergeIncompatibleForSingleClusterRule(hasRouteLevelClusterSettings bool, backendRefCount int) bool {
746+
// A route-level BackendTrafficPolicy's ClusterSettings would incorrectly apply to a
747+
// cluster shared with other routes if merged.
748+
if hasRouteLevelClusterSettings {
749+
return true
750+
}
751+
// This route type has no weighted-cluster mechanism at the listener layer, so a rule's
752+
// backendRefs must always resolve to a single cluster — letting them merge independently
753+
// could split the rule across clusters the listener can't reference together.
754+
if backendRefCount > 1 {
755+
return true
756+
}
757+
return false
742758
}
743759

744760
// backendClusterSettingName derives the DestinationSetting name for a backendRef: a merged
@@ -1324,7 +1340,7 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe
13241340
for i := range rule.BackendRefs {
13251341
backendRefs[i] = rule.BackendRefs[i].BackendObjectReference
13261342
}
1327-
mergeIncompatible := t.mergeIncompatibleForRule(grpcRoute, parentRef, rule.Name, backendRefs, hasRouteLevelClusterSettings, false, gatewayCtx)
1343+
mergeIncompatible := t.mergeIncompatibleForWeightedRule(grpcRoute, parentRef, rule.Name, backendRefs, hasRouteLevelClusterSettings, false, gatewayCtx)
13281344

13291345
backendRefNames := make([]string, len(rule.BackendRefs))
13301346
for i := range rule.BackendRefs {
@@ -1729,7 +1745,7 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour
17291745
for _, rule := range tlsRoute.Spec.Rules {
17301746
gatewayCtx, btpRoutingType, hasRouteLevelClusterSettings := t.resolveRoutingContext(tlsRoute, parentRef, rule.Name)
17311747
gwIR := t.gatewayXdsIR(gatewayCtx, xdsIR)
1732-
mergeIncompatible := mergeIncompatibleForNonWeightedRule(hasRouteLevelClusterSettings, len(rule.BackendRefs))
1748+
mergeIncompatible := mergeIncompatibleForSingleClusterRule(hasRouteLevelClusterSettings, len(rule.BackendRefs))
17331749
for i := range rule.BackendRefs {
17341750
backendRefCtx := DirectBackendRef{BackendRef: &rule.BackendRefs[i]}
17351751
// ds will never be nil here because processDestination returns an empty DestinationSetting for invalid backendRefs.
@@ -1914,7 +1930,7 @@ func (t *Translator) processUDPRouteParentRefs(udpRoute *UDPRouteContext, resour
19141930

19151931
gatewayCtx, btpRoutingType, hasRouteLevelClusterSettings := t.resolveRoutingContext(udpRoute, parentRef, udpRoute.Spec.Rules[0].Name)
19161932
gwIR := t.gatewayXdsIR(gatewayCtx, xdsIR)
1917-
mergeIncompatible := mergeIncompatibleForNonWeightedRule(hasRouteLevelClusterSettings, len(udpRoute.Spec.Rules[0].BackendRefs))
1933+
mergeIncompatible := mergeIncompatibleForSingleClusterRule(hasRouteLevelClusterSettings, len(udpRoute.Spec.Rules[0].BackendRefs))
19181934
for i := range udpRoute.Spec.Rules[0].BackendRefs {
19191935
backendRefCtx := DirectBackendRef{BackendRef: &udpRoute.Spec.Rules[0].BackendRefs[i]}
19201936
// ds will never be nil here because processDestination returns an empty DestinationSetting for invalid backendRefs.
@@ -2070,7 +2086,7 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour
20702086

20712087
gatewayCtx, btpRoutingType, hasRouteLevelClusterSettings := t.resolveRoutingContext(tcpRoute, parentRef, tcpRoute.Spec.Rules[0].Name)
20722088
gwIR := t.gatewayXdsIR(gatewayCtx, xdsIR)
2073-
mergeIncompatible := mergeIncompatibleForNonWeightedRule(hasRouteLevelClusterSettings, len(tcpRoute.Spec.Rules[0].BackendRefs))
2089+
mergeIncompatible := mergeIncompatibleForSingleClusterRule(hasRouteLevelClusterSettings, len(tcpRoute.Spec.Rules[0].BackendRefs))
20742090
for i := range tcpRoute.Spec.Rules[0].BackendRefs {
20752091
backendRefCtx := DirectBackendRef{BackendRef: &tcpRoute.Spec.Rules[0].BackendRefs[i]}
20762092
ds, cluster, _, err := t.processBackendRef(destName, i, backendRefCtx, parentRef, tcpRoute, resources, gatewayCtx, btpRoutingType, xdsIR, mergeIncompatible)

internal/gatewayapi/route_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ func TestIsFallbackBackend(t *testing.T) {
890890
}
891891
}
892892

893-
func TestMergeIncompatibleForRule(t *testing.T) {
893+
func TestMergeIncompatibleForWeightedRule(t *testing.T) {
894894
fallbackTrue := true
895895
fallbackBackend := &egv1a1.Backend{
896896
ObjectMeta: metav1.ObjectMeta{Name: "be-fallback", Namespace: "default"},
@@ -974,7 +974,7 @@ func TestMergeIncompatibleForRule(t *testing.T) {
974974
BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: "default", Name: "be-fallback"}: fallbackBackend},
975975
BTPLoadBalancerIndex: tc.lbIndex,
976976
}}
977-
got := tr.mergeIncompatibleForRule(route, parentRef, ruleName, tc.backendRefs, tc.hasRouteLevelClusterSettings, tc.sessionPersistent, tc.gatewayCtx)
977+
got := tr.mergeIncompatibleForWeightedRule(route, parentRef, ruleName, tc.backendRefs, tc.hasRouteLevelClusterSettings, tc.sessionPersistent, tc.gatewayCtx)
978978
require.Equal(t, tc.want, got)
979979
})
980980
}

internal/ir/xds.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,10 +1923,7 @@ type BackendCluster struct {
19231923
Settings []*DestinationSetting `json:"settings,omitempty" yaml:"settings,omitempty"`
19241924
// Metadata describes the backend resource (Service, Backend, etc.)
19251925
Metadata *ResourceMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"`
1926-
// Merged is true when this cluster was created via backend-identity deduplication
1927-
// (MergeBackends) and so may be referenced by more than one route. A property derived from
1928-
// exactly one referencing route (e.g. that route's own hostname) is not reliable for a merged
1929-
// cluster and must not be used as if it uniquely belonged to this cluster.
1926+
// Merged is true when this cluster is shared across routes via backend-identity deduplication.
19301927
Merged bool `json:"merged,omitempty" yaml:"merged,omitempty"`
19311928
}
19321929

internal/xds/translator/backend_registry.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,11 @@ func (t *Translator) getBackendClusters(rd *ir.RouteDestination) []*ir.BackendCl
4545
return []*ir.BackendCluster{{Name: rd.Name, Metadata: rd.Metadata}}
4646
}
4747

48-
// singleResolvedClusterName returns the name of rd's one resolved BackendCluster, when it
49-
// resolves to exactly one: that's the actual Envoy cluster the backend registered under, which
50-
// differs from rd's own route-scoped Name once the backend merges. Callers use this in place of
51-
// rd.Name wherever they reference a cluster directly (not through a weighted-clusters
52-
// specifier), so the reference stays valid after a merge. Falls back to rd.Name (already correct
53-
// when nothing merged) whenever zero or more than one cluster resolves, since a direct reference
54-
// can't represent that anyway.
48+
// singleResolvedClusterName returns the name of rd's one resolved BackendCluster, when there
49+
// is exactly one. This layer doesn't assume data from earlier layers is always well-formed:
50+
// if the ref count isn't exactly one (should be impossible in this call path) or the ref
51+
// doesn't resolve to a registered BackendCluster, something unexpected has happened, and rd's
52+
// own route-scoped name is the best available fallback.
5553
func (t *Translator) singleResolvedClusterName(rd *ir.RouteDestination) string {
5654
if bcs := t.getBackendClusters(rd); len(bcs) == 1 {
5755
return bcs[0].Name

0 commit comments

Comments
 (0)