Skip to content

Commit 592909d

Browse files
authored
Merge pull request #4298 from shuqz/gwrulescrdloader
[feat:gw api] Add tg stickiness and fixed response
2 parents 1b09bfa + c2f7acb commit 592909d

31 files changed

+3179
-434
lines changed

apis/gateway/v1beta1/listenerruleconfig_types.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type ActionType string
5959
const (
6060
ActionTypeForward ActionType = "forward"
6161
ActionTypeFixedResponse ActionType = "fixed-response"
62-
ActionTypeEnumRedirect ActionType = "redirect"
62+
ActionTypeRedirect ActionType = "redirect"
6363
ActionTypeAuthenticateCognito ActionType = "authenticate-cognito"
6464
ActionTypeAuthenticateOIDC ActionType = "authenticate-oidc"
6565
)
@@ -81,7 +81,7 @@ type TargetGroupStickinessConfig struct {
8181

8282
// Information about a forward action.
8383
type ForwardActionConfig struct {
84-
84+
// +kubebuilder:default={}
8585
// The target group stickiness for the rule.
8686
// Note: ForwardActionConfig only supports target group stickiness configuration through CRD.
8787
// All other forward action fields must be set through the Gateway API native way.
@@ -263,6 +263,7 @@ type Action struct {
263263
// +kubebuilder:validation:XValidation:rule="!has(self.actions) || size(self.actions) > 0",message="At least one action must be specified if actions field is present"
264264
// +kubebuilder:validation:XValidation:rule="!has(self.actions) || self.actions.all(a, a.type == 'authenticate-oidc' || a.type == 'authenticate-cognito' || a.type == 'fixed-response' || a.type == 'forward' || a.type == 'redirect')",message="Only forward, redirect, authenticate-oidc, authenticate-cognito, and fixed-response action types are supported"
265265
// +kubebuilder:validation:XValidation:rule="!has(self.actions) || size(self.actions.filter(a, a.type == 'authenticate-oidc' || a.type == 'authenticate-cognito')) <= 1",message="At most one authentication action (either authenticate-oidc or authenticate-cognito) can be specified"
266+
// +kubebuilder:validation:XValidation:rule="!has(self.actions) || size(self.actions.filter(a, a.type == 'fixed-response' || a.type == 'forward' || a.type == 'redirect')) <= 1",message="At most one routing action (fixed-response or forward or redirect) can be specified"
266267
type ListenerRuleSpec struct {
267268
// Actions defines the set of actions to be performed when conditions match.
268269
// This CRD implementation currently supports only authenticate-oidc, authenticate-cognito, and fixed-response action types fully and forward and redirect actions partially

config/crd/gateway/gateway-crds.yaml

Lines changed: 388 additions & 0 deletions
Large diffs are not rendered by default.

config/crd/gateway/gateway.k8s.aws_listenerruleconfigurations.yaml

Lines changed: 388 additions & 0 deletions
Large diffs are not rendered by default.

config/crd/gateway/gateway.k8s.aws_loadbalancerconfigurations.yaml

Lines changed: 339 additions & 0 deletions
Large diffs are not rendered by default.

config/crd/gateway/gateway.k8s.aws_targetgroupconfigurations.yaml

Lines changed: 475 additions & 0 deletions
Large diffs are not rendered by default.

config/crd/gateway/kustomization.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
22
kind: Kustomization
33
resources:
44
- gateway.k8s.aws_targetgroupconfigurations.yaml
5-
- gateway.k8s.aws_loadbalancerconfigurations.yaml
5+
- gateway.k8s.aws_loadbalancerconfigurations.yaml
6+
- gateway.k8s.aws_listenerruleconfigurations.yaml

helm/aws-load-balancer-controller/crds/gateway-crds.yaml

Lines changed: 388 additions & 0 deletions
Large diffs are not rendered by default.

pkg/gateway/model/model_build_listener.go

Lines changed: 26 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ func (l listenerBuilderImpl) buildL4ListenerSpec(ctx context.Context, stack core
181181
return listenerSpec, nil
182182
}
183183

184-
// this is only for L7 ALB
185184
func (l listenerBuilderImpl) buildListenerRules(stack core.Stack, ls *elbv2model.Listener, ipAddressType elbv2model.IPAddressType, securityGroups securityGroupOutput, gw *gwv1.Gateway, port int32, lbCfg elbv2gw.LoadBalancerConfiguration, routes map[int32][]routeutils.RouteDescriptor) error {
186185
// sort all rules based on precedence
187186
rulesWithPrecedenceOrder := routeutils.SortAllRulesByPrecedence(routes[port])
@@ -191,6 +190,7 @@ func (l listenerBuilderImpl) buildListenerRules(stack core.Stack, ls *elbv2model
191190
route := ruleWithPrecedence.CommonRulePrecedence.RouteDescriptor
192191
rule := ruleWithPrecedence.CommonRulePrecedence.Rule
193192

193+
// Build Rule Conditions
194194
var conditionsList []elbv2model.RuleCondition
195195
var err error
196196
switch route.GetRouteKind() {
@@ -202,9 +202,12 @@ func (l listenerBuilderImpl) buildListenerRules(stack core.Stack, ls *elbv2model
202202
if err != nil {
203203
return err
204204
}
205-
tags, tagsErr := l.tagHelper.getGatewayTags(lbCfg)
206-
if tagsErr != nil {
207-
return tagsErr
205+
206+
// set up for building routing actions
207+
var actions []elbv2model.Action
208+
var routingAction *elbv2gw.Action
209+
if rule.GetListenerRuleConfig() != nil {
210+
routingAction = getRoutingAction(rule.GetListenerRuleConfig())
208211
}
209212
targetGroupTuples := make([]elbv2model.TargetGroupTuple, 0, len(rule.GetBackends()))
210213
for _, backend := range rule.GetBackends() {
@@ -219,32 +222,24 @@ func (l listenerBuilderImpl) buildListenerRules(stack core.Stack, ls *elbv2model
219222
Weight: &weight,
220223
})
221224
}
222-
223-
var actions []elbv2model.Action
224-
225-
if shouldProvisionActions(targetGroupTuples) {
226-
actions = buildL7ListenerForwardActions(targetGroupTuples, nil)
225+
// Build Rule Routing Actions
226+
actions, err = routeutils.BuildRuleRoutingActions(rule, route, routingAction, targetGroupTuples)
227+
if err != nil {
228+
return err
227229
}
228230

229-
// configure actions based on filters
230-
switch route.GetRouteKind() {
231-
case routeutils.HTTPRouteKind:
232-
httpRule := rule.GetRawRouteRule().(*gwv1.HTTPRouteRule)
233-
if len(httpRule.Filters) > 0 {
234-
finalActions, err := routeutils.BuildHttpRuleActionsBasedOnFilter(httpRule.Filters)
235-
if err != nil {
236-
return err
237-
}
238-
actions = finalActions
239-
}
240-
// TODO: add case for GRPC
241-
}
231+
// TODO: build rule auth actions
242232

243233
if len(actions) == 0 {
244234
l.logger.Info("Filling in no backend actions with fixed 503")
245235
actions = buildL7ListenerNoBackendActions()
246236
}
247237

238+
tags, tagsErr := l.tagHelper.getGatewayTags(lbCfg)
239+
if tagsErr != nil {
240+
return tagsErr
241+
}
242+
248243
albRules = append(albRules, elbv2model.Rule{
249244
Conditions: conditionsList,
250245
Actions: actions,
@@ -382,28 +377,6 @@ func buildL4ListenerDefaultActions(targetGroup *elbv2model.TargetGroup) []elbv2m
382377
}
383378
}
384379

385-
func buildL7ListenerForwardActions(targetGroupTuple []elbv2model.TargetGroupTuple, duration *int32) []elbv2model.Action {
386-
387-
forwardConfig := &elbv2model.ForwardActionConfig{
388-
TargetGroups: targetGroupTuple,
389-
}
390-
391-
// Configure target group stickiness if a duration is specified
392-
if duration != nil {
393-
forwardConfig.TargetGroupStickinessConfig = &elbv2model.TargetGroupStickinessConfig{
394-
Enabled: awssdk.Bool(true),
395-
DurationSeconds: awssdk.Int32(*duration),
396-
}
397-
}
398-
399-
return []elbv2model.Action{
400-
{
401-
Type: elbv2model.ActionTypeForward,
402-
ForwardConfig: forwardConfig,
403-
},
404-
}
405-
}
406-
407380
func (l listenerBuilderImpl) buildMutualAuthenticationAttributes(ctx context.Context, subnetsResolver networking.SubnetsResolver, subnets buildLoadBalancerSubnetsOutput, gwLsCfg *gwListenerConfig, lbLsCfg *elbv2gw.ListenerConfiguration) (*elbv2model.MutualAuthenticationAttributes, error) {
408381
// Skip mTLS configuration for non-secure protocols
409382
if !isSecureProtocol(gwLsCfg.protocol) {
@@ -553,15 +526,15 @@ func newListenerBuilder(ctx context.Context, loadBalancerType elbv2model.LoadBal
553526
}
554527
}
555528

556-
// shouldProvisionActions -- determine if the given target groups are acceptable for ELB Actions.
557-
// The criteria -
558-
// 1/ One or more target groups are present.
559-
// 2/ At least one target group has a weight greater than zero.
560-
func shouldProvisionActions(targetGroups []elbv2model.TargetGroupTuple) bool {
561-
for _, tg := range targetGroups {
562-
if tg.Weight == nil || *tg.Weight != 0 {
563-
return true
529+
// getRoutingAction: returns routing action from listener rule configuration
530+
// action will only be one of forward, fixed response or redirect
531+
func getRoutingAction(config *elbv2gw.ListenerRuleConfiguration) *elbv2gw.Action {
532+
if config != nil && config.Spec.Actions != nil {
533+
for _, action := range config.Spec.Actions {
534+
if action.Type == elbv2gw.ActionTypeForward || action.Type == elbv2gw.ActionTypeFixedResponse || action.Type == elbv2gw.ActionTypeRedirect {
535+
return &action
536+
}
564537
}
565538
}
566-
return false
539+
return nil
567540
}

pkg/gateway/model/model_build_listener_test.go

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package model
22

33
import (
44
"context"
5-
"fmt"
65
awssdk "github.com/aws/aws-sdk-go-v2/aws"
76
elbv2sdk "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
87
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
@@ -1090,7 +1089,7 @@ func Test_buildMutualAuthenticationAttributes(t *testing.T) {
10901089
}
10911090
}
10921091

1093-
func TestBuildListenerRules(t *testing.T) {
1092+
func Test_BuildListenerRules(t *testing.T) {
10941093
testCases := []struct {
10951094
name string
10961095
sgOutput securityGroupOutput
@@ -1219,6 +1218,150 @@ func TestBuildListenerRules(t *testing.T) {
12191218
},
12201219
},
12211220
},
1221+
{
1222+
name: "redirect filter should result in redirect action",
1223+
port: 80,
1224+
ipAddressType: elbv2model.IPAddressTypeIPV4,
1225+
sgOutput: securityGroupOutput{
1226+
backendSecurityGroupToken: coremodel.LiteralStringToken("sg-B"),
1227+
},
1228+
routes: map[int32][]routeutils.RouteDescriptor{
1229+
80: {
1230+
&routeutils.MockRoute{
1231+
Kind: routeutils.HTTPRouteKind,
1232+
Name: "my-route",
1233+
Namespace: "my-route-ns",
1234+
Rules: []routeutils.RouteRule{
1235+
&routeutils.MockRule{
1236+
RawRule: &gwv1.HTTPRouteRule{
1237+
Filters: []gwv1.HTTPRouteFilter{
1238+
{
1239+
Type: gwv1.HTTPRouteFilterRequestRedirect,
1240+
RequestRedirect: &gwv1.HTTPRequestRedirectFilter{
1241+
Scheme: awssdk.String("HTTPS"),
1242+
StatusCode: awssdk.Int(301),
1243+
},
1244+
},
1245+
},
1246+
Matches: []gwv1.HTTPRouteMatch{
1247+
{
1248+
Path: &gwv1.HTTPPathMatch{
1249+
Type: (*gwv1.PathMatchType)(awssdk.String("PathPrefix")),
1250+
Value: awssdk.String("/"),
1251+
},
1252+
},
1253+
},
1254+
},
1255+
BackendRefs: []routeutils.Backend{
1256+
{
1257+
Service: &corev1.Service{},
1258+
ServicePort: &corev1.ServicePort{Name: "svcport"},
1259+
Weight: 1,
1260+
},
1261+
},
1262+
},
1263+
},
1264+
},
1265+
},
1266+
},
1267+
expectedRules: []*elbv2model.ListenerRuleSpec{
1268+
{
1269+
Priority: 1,
1270+
Actions: []elbv2model.Action{
1271+
{
1272+
Type: "redirect",
1273+
RedirectConfig: &elbv2model.RedirectActionConfig{
1274+
Protocol: awssdk.String("HTTPS"),
1275+
StatusCode: "HTTP_301",
1276+
},
1277+
},
1278+
},
1279+
Conditions: []elbv2model.RuleCondition{
1280+
{
1281+
Field: "path-pattern",
1282+
PathPatternConfig: &elbv2model.PathPatternConditionConfig{
1283+
Values: []string{"/*"},
1284+
},
1285+
},
1286+
},
1287+
},
1288+
},
1289+
},
1290+
{
1291+
name: "listener rule config with fixed response should override forward",
1292+
port: 80,
1293+
ipAddressType: elbv2model.IPAddressTypeIPV4,
1294+
sgOutput: securityGroupOutput{
1295+
backendSecurityGroupToken: coremodel.LiteralStringToken("sg-B"),
1296+
},
1297+
routes: map[int32][]routeutils.RouteDescriptor{
1298+
80: {
1299+
&routeutils.MockRoute{
1300+
Kind: routeutils.HTTPRouteKind,
1301+
Name: "my-route",
1302+
Namespace: "my-route-ns",
1303+
Rules: []routeutils.RouteRule{
1304+
&routeutils.MockRule{
1305+
RawRule: &gwv1.HTTPRouteRule{
1306+
Matches: []gwv1.HTTPRouteMatch{
1307+
{
1308+
Path: &gwv1.HTTPPathMatch{
1309+
Type: (*gwv1.PathMatchType)(awssdk.String("PathPrefix")),
1310+
Value: awssdk.String("/"),
1311+
},
1312+
},
1313+
},
1314+
},
1315+
BackendRefs: []routeutils.Backend{
1316+
{
1317+
Service: &corev1.Service{},
1318+
ServicePort: &corev1.ServicePort{Name: "svcport"},
1319+
Weight: 1,
1320+
},
1321+
},
1322+
ListenerRuleConfig: &elbv2gw.ListenerRuleConfiguration{
1323+
Spec: elbv2gw.ListenerRuleSpec{
1324+
Actions: []elbv2gw.Action{
1325+
{
1326+
Type: elbv2gw.ActionTypeFixedResponse,
1327+
FixedResponseConfig: &elbv2gw.FixedResponseActionConfig{
1328+
StatusCode: 404,
1329+
ContentType: awssdk.String("text/html"),
1330+
MessageBody: awssdk.String("Not Found"),
1331+
},
1332+
},
1333+
},
1334+
},
1335+
},
1336+
},
1337+
},
1338+
},
1339+
},
1340+
},
1341+
expectedRules: []*elbv2model.ListenerRuleSpec{
1342+
{
1343+
Priority: 1,
1344+
Actions: []elbv2model.Action{
1345+
{
1346+
Type: "fixed-response",
1347+
FixedResponseConfig: &elbv2model.FixedResponseActionConfig{
1348+
StatusCode: "404",
1349+
ContentType: awssdk.String("text/html"),
1350+
MessageBody: awssdk.String("Not Found"),
1351+
},
1352+
},
1353+
},
1354+
Conditions: []elbv2model.RuleCondition{
1355+
{
1356+
Field: "path-pattern",
1357+
PathPatternConfig: &elbv2model.PathPatternConditionConfig{
1358+
Values: []string{"/*"},
1359+
},
1360+
},
1361+
},
1362+
},
1363+
},
1364+
},
12221365
}
12231366

12241367
for _, tc := range testCases {
@@ -1261,7 +1404,6 @@ func TestBuildListenerRules(t *testing.T) {
12611404
conditionsEqual := cmp.Equal(elr.Conditions, alr.Spec.Conditions)
12621405
actionsEqual := cmp.Equal(elr.Actions, alr.Spec.Actions, opt)
12631406
priorityEqual := elr.Priority == alr.Spec.Priority
1264-
fmt.Printf("%+v,%+v,%+v\n", conditionsEqual, actionsEqual, priorityEqual)
12651407
if conditionsEqual && actionsEqual && priorityEqual {
12661408
processedSet[alr] = true
12671409
break

0 commit comments

Comments
 (0)