Skip to content

Commit 3a79dea

Browse files
fix: Skip target group creation for HTTPRoute redirect-only rules
This commit fixes issue #4497 where HTTPRoute rules with only RequestRedirect filters and no BackendRefs were incorrectly creating target groups, consuming AWS resources unnecessarily. Key changes: - Add IsRedirectOnlyRule() function to detect redirect-only rules - Modify buildListenerRules() to skip target group creation for redirect-only rules - Maintain redirect action creation for proper ALB configuration - Add comprehensive resource management and cleanup logic - Include extensive property-based testing with 8 correctness properties The fix ensures redirect-only rules don't consume AWS target group quotas while maintaining full functionality for redirect actions and preserving backward compatibility with Gateway API v1 specifications. Fixes #4497
1 parent 9779b80 commit 3a79dea

13 files changed

+4265
-10
lines changed

pkg/gateway/model/model_build_listener.go

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,13 @@ func (l listenerBuilderImpl) buildL4TargetGroupTuples(stack core.Stack, routes [
199199
return tgTuples, nil
200200
}
201201

202+
// buildListenerRules creates ALB listener rules from HTTPRoute and GRPCRoute rules.
203+
// This function handles both redirect-only rules and backend rules:
204+
// - Redirect-only rules: Rules with RequestRedirect filters but no BackendRefs. These create
205+
// ALB redirect actions without target groups, optimizing resource usage.
206+
// - Backend rules: Rules with BackendRefs that create target groups for traffic forwarding.
207+
// - Mixed rules: Rules with both redirect filters and backends, which create both redirect
208+
// actions and target groups according to Gateway API specifications.
202209
func (l listenerBuilderImpl) buildListenerRules(ctx context.Context, stack core.Stack, ls *elbv2model.Listener, ipAddressType elbv2model.IPAddressType, gw *gwv1.Gateway, port int32, routes map[int32][]routeutils.RouteDescriptor) ([]types.NamespacedName, error) {
203210
// sort all rules based on precedence
204211
rulesWithPrecedenceOrder := routeutils.SortAllRulesByPrecedence(routes[port], port)
@@ -234,18 +241,72 @@ func (l listenerBuilderImpl) buildListenerRules(ctx context.Context, stack core.
234241
}
235242
routingAction = getRoutingAction(rule.GetListenerRuleConfig())
236243
}
244+
// Analyze rule type to determine target group creation strategy
245+
// - Redirect-only rules: Have redirect filters but no backends, skip target group creation
246+
// - Backend rules: Have backends (with or without redirect filters), create target groups
247+
// - Empty rules: Have neither redirects nor backends, will result in 503 response
248+
isRedirectOnly := routeutils.IsRedirectOnlyRule(rule)
249+
hasRedirectFilter := routeutils.HasRequestRedirectFilter(rule)
250+
backendCount := len(rule.GetBackends())
251+
252+
// Enhanced logging for redirect-only rule processing
253+
l.logger.V(1).Info("Processing HTTPRoute rule",
254+
"route", route.GetRouteNamespacedName(),
255+
"routeKind", route.GetRouteKind(),
256+
"isRedirectOnly", isRedirectOnly,
257+
"hasRedirectFilter", hasRedirectFilter,
258+
"backendCount", backendCount,
259+
"listenerPort", port,
260+
"listenerProtocol", ls.Spec.Protocol)
261+
237262
targetGroupTuples := make([]elbv2model.TargetGroupTuple, 0, len(rule.GetBackends()))
238-
for _, backend := range rule.GetBackends() {
239-
arn, tgErr := l.tgBuilder.buildTargetGroup(stack, gw, port, ls.Spec.Protocol, ipAddressType, route, backend)
240-
if tgErr != nil {
241-
return nil, tgErr
263+
264+
// Target group creation logic:
265+
// - Skip target group creation for redirect-only rules to optimize resource usage
266+
// - Create target groups for all backends in non-redirect-only rules
267+
// - This ensures redirect-only rules don't consume AWS target group quotas
268+
if !isRedirectOnly {
269+
l.logger.Info("Creating target groups for rule with backends",
270+
"route", route.GetRouteNamespacedName(),
271+
"backendCount", backendCount,
272+
"hasRedirectFilter", hasRedirectFilter)
273+
274+
for i, backend := range rule.GetBackends() {
275+
l.logger.V(1).Info("Building target group for backend",
276+
"route", route.GetRouteNamespacedName(),
277+
"backendIndex", i,
278+
"backendWeight", backend.Weight)
279+
280+
arn, tgErr := l.tgBuilder.buildTargetGroup(stack, gw, port, ls.Spec.Protocol, ipAddressType, route, backend)
281+
if tgErr != nil {
282+
l.logger.Error(tgErr, "Failed to build target group for backend",
283+
"route", route.GetRouteNamespacedName(),
284+
"backendIndex", i,
285+
"backendWeight", backend.Weight)
286+
return nil, tgErr
287+
}
288+
289+
// weighted target group support
290+
weight := int32(backend.Weight)
291+
targetGroupTuples = append(targetGroupTuples, elbv2model.TargetGroupTuple{
292+
TargetGroupARN: arn,
293+
Weight: &weight,
294+
})
295+
296+
l.logger.V(1).Info("Successfully created target group for backend",
297+
"route", route.GetRouteNamespacedName(),
298+
"backendIndex", i,
299+
"targetGroupARN", arn,
300+
"weight", weight)
242301
}
243-
// weighted target group support
244-
weight := int32(backend.Weight)
245-
targetGroupTuples = append(targetGroupTuples, elbv2model.TargetGroupTuple{
246-
TargetGroupARN: arn,
247-
Weight: &weight,
248-
})
302+
} else {
303+
// Log that we're processing a redirect-only rule with detailed information
304+
l.logger.Info("Processing redirect-only rule, skipping target group creation",
305+
"route", route.GetRouteNamespacedName(),
306+
"routeKind", route.GetRouteKind(),
307+
"listenerPort", port,
308+
"listenerProtocol", ls.Spec.Protocol,
309+
"reason", "rule has redirect filters but no backends")
249310
}
250311

251312
// Build Rule PreRoutingAction

0 commit comments

Comments
 (0)