Skip to content

Commit 09030dd

Browse files
committed
Merge branch 'main' of github.com:kubernetes-sigs/aws-load-balancer-controller
2 parents ca9d382 + 04ce65e commit 09030dd

15 files changed

+2317
-40
lines changed

controllers/gateway/gateway_controller.go

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package gateway
33
import (
44
"context"
55
"fmt"
6+
"time"
7+
68
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
79
"github.com/go-logr/logr"
810
"github.com/pkg/errors"
@@ -22,6 +24,7 @@ import (
2224
"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
2325
ctrlerrors "sigs.k8s.io/aws-load-balancer-controller/pkg/error"
2426
"sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/constants"
27+
gateway_constants "sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/constants"
2528
gatewaymodel "sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/model"
2629
"sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/referencecounter"
2730
"sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/routeutils"
@@ -43,7 +46,6 @@ import (
4346
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
4447
gwalpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
4548
gwbeta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
46-
"time"
4749
)
4850

4951
const (
@@ -206,25 +208,35 @@ func (r *gatewayReconciler) reconcileHelper(ctx context.Context, req reconcile.R
206208
mergedLbConfig, err := r.cfgResolver.getLoadBalancerConfigForGateway(ctx, r.k8sClient, r.finalizerManager, gw, gwClass)
207209

208210
if err != nil {
209-
statusErr := r.updateGatewayStatusFailure(ctx, gw, gwv1.GatewayReasonInvalid, err.Error())
211+
statusErr := r.updateGatewayStatusFailure(ctx, gw, gwv1.GatewayReasonInvalid, err.Error(), nil)
210212
if statusErr != nil {
211213
r.logger.Error(statusErr, "Unable to update gateway status on failure to retrieve attached config")
212214
}
213215
return err
214216
}
215217

216-
allRoutes, err := r.gatewayLoader.LoadRoutesForGateway(ctx, *gw, r.routeFilter)
218+
loaderResults, err := r.gatewayLoader.LoadRoutesForGateway(ctx, *gw, r.routeFilter, r.controllerName)
217219

218-
if err != nil {
220+
if err != nil || loaderResults.ValidationResults.HasErrors {
219221
var loaderErr routeutils.LoaderError
220-
if errors.As(err, &loaderErr) {
221-
statusErr := r.updateGatewayStatusFailure(ctx, gw, loaderErr.GetGatewayReason(), loaderErr.GetGatewayMessage())
222+
if errors.As(err, &loaderErr) || loaderResults.ValidationResults.HasErrors {
223+
var gatewayReason gwv1.GatewayConditionReason
224+
var gatewayMessage string
225+
if loaderErr == nil && loaderResults.ValidationResults.HasErrors {
226+
gatewayReason = gwv1.GatewayReasonAccepted
227+
gatewayMessage = gateway_constants.GatewayAcceptedFalseMessage
228+
} else {
229+
gatewayReason = loaderErr.GetGatewayReason()
230+
gatewayMessage = loaderErr.GetGatewayMessage()
231+
}
232+
statusErr := r.updateGatewayStatusFailure(ctx, gw, gatewayReason, gatewayMessage, loaderResults)
222233
if statusErr != nil {
223234
r.logger.Error(statusErr, "Unable to update gateway status on failure to build routes")
224235
}
225236
}
226237
return err
227238
}
239+
allRoutes := loaderResults.Routes
228240

229241
// To handle Addons, we need to build the set that has been previously enabled. This is stored within the Gateway annotations.
230242
allAddOns := getStoredAddonConfig(gw, r.logger)
@@ -241,7 +253,6 @@ func (r *gatewayReconciler) reconcileHelper(ctx context.Context, req reconcile.R
241253
if err != nil {
242254
return err
243255
}
244-
245256
r.logger.V(1).Info("Got this addon config", "current", currentAddOns, "new addon", newAddOnConfig)
246257

247258
// To accurately track the set of enabled addons, we need to figure out if any addons were added / removed during this run.
@@ -267,7 +278,7 @@ func (r *gatewayReconciler) reconcileHelper(ctx context.Context, req reconcile.R
267278
return nil
268279
}
269280
r.serviceReferenceCounter.UpdateRelations(getServicesFromRoutes(allRoutes), k8s.NamespacedName(gw), false)
270-
err = r.reconcileUpdate(ctx, gw, gwClass, stack, lb, backendSGRequired, secrets)
281+
err = r.reconcileUpdate(ctx, gw, gwClass, stack, lb, backendSGRequired, secrets, loaderResults.AttachedRoutesMap)
271282
if err != nil {
272283
r.logger.Error(err, "Failed to process gateway update", "gw", k8s.NamespacedName(gw))
273284
return err
@@ -309,7 +320,7 @@ func (r *gatewayReconciler) reconcileDelete(ctx context.Context, gw *gwv1.Gatewa
309320
}
310321

311322
func (r *gatewayReconciler) reconcileUpdate(ctx context.Context, gw *gwv1.Gateway, gwClass *gwv1.GatewayClass, stack core.Stack,
312-
lb *elbv2model.LoadBalancer, backendSGRequired bool, secrets []types.NamespacedName) error {
323+
lb *elbv2model.LoadBalancer, backendSGRequired bool, secrets []types.NamespacedName, attachedRoutesMap map[gwv1.SectionName]int32) error {
313324
// add gateway finalizer
314325
if err := r.finalizerManager.AddFinalizers(ctx, gw, r.finalizer); err != nil {
315326
r.eventRecorder.Event(gw, corev1.EventTypeWarning, k8s.GatewayEventReasonFailedAddFinalizer, fmt.Sprintf("Failed add gateway finalizer due to %v", err))
@@ -327,7 +338,7 @@ func (r *gatewayReconciler) reconcileUpdate(ctx context.Context, gw *gwv1.Gatewa
327338
}
328339
}
329340

330-
if err = r.updateGatewayStatusSuccess(ctx, lb.Status, gw); err != nil {
341+
if err = r.updateGatewayStatusSuccess(ctx, lb.Status, gw, attachedRoutesMap); err != nil {
331342
r.eventRecorder.Event(gw, corev1.EventTypeWarning, k8s.GatewayEventReasonFailedUpdateStatus, fmt.Sprintf("Failed update status due to %v", err))
332343
return err
333344
}
@@ -366,7 +377,7 @@ func (r *gatewayReconciler) buildModel(ctx context.Context, gw *gwv1.Gateway, cf
366377
return stack, lb, newAddOnConfig, backendSGRequired, secrets, nil
367378
}
368379

369-
func (r *gatewayReconciler) updateGatewayStatusSuccess(ctx context.Context, lbStatus *elbv2model.LoadBalancerStatus, gw *gwv1.Gateway) error {
380+
func (r *gatewayReconciler) updateGatewayStatusSuccess(ctx context.Context, lbStatus *elbv2model.LoadBalancerStatus, gw *gwv1.Gateway, attachedRoutesMap map[gwv1.SectionName]int32) error {
370381
// LB Status should always be set, if it's not, we need to prevent NPE
371382
if lbStatus == nil {
372383
r.logger.Info("Unable to update Gateway Status due to null LB status")
@@ -395,6 +406,13 @@ func (r *gatewayReconciler) updateGatewayStatusSuccess(ctx context.Context, lbSt
395406
needPatch = true
396407
}
397408

409+
// update listeners status
410+
ListenerStatuses := buildListenerStatus(r.controllerName, *gw, attachedRoutesMap, nil)
411+
if !isListenerStatusIdentical(gw.Status.Listeners, ListenerStatuses) {
412+
gw.Status.Listeners = ListenerStatuses
413+
needPatch = true
414+
}
415+
398416
if needPatch {
399417
if err := r.k8sClient.Status().Patch(ctx, gw, client.MergeFrom(gwOld)); err != nil {
400418
return errors.Wrapf(err, "failed to update gw status: %v", k8s.NamespacedName(gw))
@@ -408,11 +426,22 @@ func (r *gatewayReconciler) updateGatewayStatusSuccess(ctx context.Context, lbSt
408426
return nil
409427
}
410428

411-
func (r *gatewayReconciler) updateGatewayStatusFailure(ctx context.Context, gw *gwv1.Gateway, reason gwv1.GatewayConditionReason, errMessage string) error {
429+
func (r *gatewayReconciler) updateGatewayStatusFailure(ctx context.Context, gw *gwv1.Gateway, reason gwv1.GatewayConditionReason, errMessage string, loadResults *routeutils.LoaderResult) error {
412430
gwOld := gw.DeepCopy()
413431

414432
needPatch := r.gatewayConditionUpdater(gw, string(gwv1.GatewayConditionAccepted), metav1.ConditionFalse, string(reason), errMessage)
415433

434+
// update listener status
435+
if loadResults != nil {
436+
listenerValidationResults := loadResults.ValidationResults
437+
attachedRoutesMap := loadResults.AttachedRoutesMap
438+
ListenerStatuses := buildListenerStatus(r.controllerName, *gw, attachedRoutesMap, &listenerValidationResults)
439+
if !isListenerStatusIdentical(gw.Status.Listeners, ListenerStatuses) {
440+
gw.Status.Listeners = ListenerStatuses
441+
needPatch = true
442+
}
443+
}
444+
416445
if needPatch {
417446
if err := r.k8sClient.Status().Patch(ctx, gw, client.MergeFrom(gwOld)); err != nil {
418447
return errors.Wrapf(err, "failed to update gw status: %v", k8s.NamespacedName(gw))
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package gateway
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
"time"
7+
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
gateway_constants "sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/constants"
10+
"sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/routeutils"
11+
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
12+
)
13+
14+
func buildListenerStatus(controllerName string, gateway gwv1.Gateway, attachedRoutesMap map[gwv1.SectionName]int32, validateListenerResults *routeutils.ListenerValidationResults) []gwv1.ListenerStatus {
15+
var listenerStatuses []gwv1.ListenerStatus
16+
17+
// if validateListenerResults is nil, getListenerConditions will build condition with accepted condition
18+
for _, listener := range gateway.Spec.Listeners {
19+
supportedKinds, _ := routeutils.GetSupportedKinds(controllerName, listener)
20+
var condition []metav1.Condition
21+
if validateListenerResults == nil {
22+
condition = getListenerConditions(gateway, nil)
23+
} else {
24+
listenerValidationResult := validateListenerResults.Results[listener.Name]
25+
condition = getListenerConditions(gateway, &listenerValidationResult)
26+
}
27+
28+
listenerStatus := gwv1.ListenerStatus{
29+
Name: listener.Name,
30+
SupportedKinds: supportedKinds,
31+
AttachedRoutes: attachedRoutesMap[listener.Name],
32+
Conditions: condition,
33+
}
34+
listenerStatuses = append(listenerStatuses, listenerStatus)
35+
}
36+
return listenerStatuses
37+
}
38+
39+
func getListenerConditions(gw gwv1.Gateway, listenerValidationResult *routeutils.ListenerValidationResult) []metav1.Condition {
40+
var conditions []metav1.Condition
41+
42+
// Determine condition type based on reason
43+
if listenerValidationResult == nil {
44+
return append(conditions, buildAcceptedCondition(gw, gwv1.ListenerReasonAccepted, gateway_constants.ListenerAcceptedMessage))
45+
}
46+
listenerReason := listenerValidationResult.Reason
47+
listenerErrMessage := listenerValidationResult.Message
48+
switch listenerReason {
49+
case gwv1.ListenerReasonHostnameConflict, gwv1.ListenerReasonProtocolConflict:
50+
conditions = append(conditions, buildConflictedCondition(gw, listenerReason, listenerErrMessage))
51+
case gwv1.ListenerReasonPortUnavailable, gwv1.ListenerReasonUnsupportedProtocol:
52+
conditions = append(conditions, buildAcceptedCondition(gw, listenerReason, listenerErrMessage))
53+
case gwv1.ListenerReasonInvalidRouteKinds, gwv1.ListenerReasonRefNotPermitted:
54+
conditions = append(conditions, buildResolvedRefsCondition(gw, listenerReason, listenerErrMessage))
55+
default:
56+
conditions = append(conditions, buildAcceptedCondition(gw, gwv1.ListenerReasonAccepted, gateway_constants.ListenerAcceptedMessage))
57+
}
58+
59+
return conditions
60+
}
61+
62+
func buildAcceptedCondition(gw gwv1.Gateway, reason gwv1.ListenerConditionReason, message string) metav1.Condition {
63+
status := metav1.ConditionTrue
64+
if reason != gwv1.ListenerReasonAccepted {
65+
status = metav1.ConditionFalse
66+
}
67+
68+
return metav1.Condition{
69+
Type: string(gwv1.ListenerConditionAccepted),
70+
Status: status,
71+
Reason: string(reason),
72+
Message: message,
73+
LastTransitionTime: metav1.NewTime(time.Now()),
74+
ObservedGeneration: gw.GetGeneration(),
75+
}
76+
}
77+
78+
func buildConflictedCondition(gw gwv1.Gateway, reason gwv1.ListenerConditionReason, message string) metav1.Condition {
79+
status := metav1.ConditionTrue
80+
if reason != gwv1.ListenerReasonAccepted {
81+
status = metav1.ConditionFalse
82+
}
83+
return metav1.Condition{
84+
Type: string(gwv1.ListenerConditionConflicted),
85+
Status: status,
86+
Reason: string(reason),
87+
Message: message,
88+
LastTransitionTime: metav1.NewTime(time.Now()),
89+
ObservedGeneration: gw.GetGeneration(),
90+
}
91+
}
92+
93+
func buildResolvedRefsCondition(gw gwv1.Gateway, reason gwv1.ListenerConditionReason, message string) metav1.Condition {
94+
status := metav1.ConditionTrue
95+
if reason != gwv1.ListenerReasonAccepted {
96+
status = metav1.ConditionFalse
97+
}
98+
return metav1.Condition{
99+
Type: string(gwv1.ListenerConditionResolvedRefs),
100+
Status: status,
101+
Reason: string(reason),
102+
Message: message,
103+
LastTransitionTime: metav1.NewTime(time.Now()),
104+
ObservedGeneration: gw.GetGeneration(),
105+
}
106+
}
107+
108+
func isListenerStatusIdentical(listenerStatus []gwv1.ListenerStatus, listenerStatusOld []gwv1.ListenerStatus) bool {
109+
if len(listenerStatus) != len(listenerStatusOld) {
110+
return false
111+
}
112+
// Sort both slices by Name before comparison
113+
sort.Slice(listenerStatus, func(i, j int) bool {
114+
return listenerStatus[i].Name < listenerStatus[j].Name
115+
})
116+
sort.Slice(listenerStatusOld, func(i, j int) bool {
117+
return listenerStatusOld[i].Name < listenerStatusOld[j].Name
118+
})
119+
for i := range listenerStatus {
120+
if listenerStatus[i].Name != listenerStatusOld[i].Name {
121+
return false
122+
}
123+
124+
if !compareSupportedKinds(listenerStatus[i].SupportedKinds, listenerStatusOld[i].SupportedKinds) {
125+
return false
126+
}
127+
128+
if listenerStatus[i].AttachedRoutes != listenerStatusOld[i].AttachedRoutes {
129+
return false
130+
}
131+
if len(listenerStatus[i].Conditions) != len(listenerStatusOld[i].Conditions) {
132+
return false
133+
}
134+
for j := range listenerStatus[i].Conditions {
135+
if listenerStatus[i].Conditions[j].Type != listenerStatusOld[i].Conditions[j].Type {
136+
return false
137+
}
138+
if listenerStatus[i].Conditions[j].Status != listenerStatusOld[i].Conditions[j].Status {
139+
return false
140+
}
141+
if listenerStatus[i].Conditions[j].Reason != listenerStatusOld[i].Conditions[j].Reason {
142+
return false
143+
}
144+
if listenerStatus[i].Conditions[j].Message != listenerStatusOld[i].Conditions[j].Message {
145+
return false
146+
}
147+
if listenerStatus[i].Conditions[j].ObservedGeneration != listenerStatusOld[i].Conditions[j].ObservedGeneration {
148+
return false
149+
}
150+
}
151+
}
152+
return true
153+
}
154+
155+
func compareSupportedKinds(kinds1, kinds2 []gwv1.RouteGroupKind) bool {
156+
if len(kinds1) != len(kinds2) {
157+
return false
158+
}
159+
160+
kindMap := make(map[string]int)
161+
for _, kind := range kinds1 {
162+
key := fmt.Sprintf("%s/%s", *kind.Group, kind.Kind)
163+
kindMap[key]++
164+
}
165+
166+
for _, kind := range kinds2 {
167+
key := fmt.Sprintf("%s/%s", *kind.Group, kind.Kind)
168+
if kindMap[key] == 0 {
169+
return false
170+
}
171+
kindMap[key]--
172+
}
173+
174+
return true
175+
}

0 commit comments

Comments
 (0)