Skip to content

Commit 459977e

Browse files
committed
enable tgb labels / annotations. enable gateway status propagation
1 parent a21f588 commit 459977e

23 files changed

+1136
-64
lines changed

controllers/gateway/config_resolver.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ func newGatewayConfigResolver() gatewayConfigResolver {
2929
func (resolver *gatewayConfigResolverImpl) getLoadBalancerConfigForGateway(ctx context.Context, k8sClient client.Client, gw *gwv1.Gateway, gwClass *gwv1.GatewayClass) (elbv2gw.LoadBalancerConfiguration, error) {
3030

3131
// If the Gateway Class isn't accepted, we shouldn't try to reconcile this Gateway.
32-
derivedStatus, _ := deriveGatewayClassAcceptedStatus(gwClass)
32+
derivedStatusIndx, ok := deriveAcceptedConditionIndex(gwClass)
3333

34-
if derivedStatus != metav1.ConditionTrue {
35-
return elbv2gw.LoadBalancerConfiguration{}, errors.Errorf("Unable to materialize gateway when gateway class [%s] is not accepted. GatewayClass status is %s", gwClass.Name, derivedStatus)
34+
if !ok || gwClass.Status.Conditions[derivedStatusIndx].Status != metav1.ConditionTrue {
35+
return elbv2gw.LoadBalancerConfiguration{}, errors.Errorf("Unable to materialize gateway when gateway class [%s] is not accepted", gwClass.Name)
3636
}
3737

3838
gatewayClassLBConfig, err := resolver.configResolverFn(ctx, k8sClient, gwClass.Spec.ParametersRef)

controllers/gateway/gateway_controller.go

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package gateway
33
import (
44
"context"
55
"fmt"
6+
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
67
"github.com/go-logr/logr"
78
"github.com/pkg/errors"
89
corev1 "k8s.io/api/core/v1"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
911
"k8s.io/apimachinery/pkg/types"
1012
"k8s.io/apimachinery/pkg/util/sets"
1113
"k8s.io/client-go/tools/record"
@@ -35,6 +37,12 @@ import (
3537
"sigs.k8s.io/controller-runtime/pkg/source"
3638
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
3739
gwalpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
40+
"time"
41+
)
42+
43+
const (
44+
requeueMessage = "Monitoring provisioning state"
45+
statusUpdateRequeueTime = 2 * time.Minute
3846
)
3947

4048
var _ Reconciler = &gatewayReconciler{}
@@ -86,6 +94,7 @@ func newGatewayReconciler(controllerName string, lbType elbv2model.LoadBalancerT
8694
reconcileTracker: reconcileTracker,
8795
cfgResolver: cfgResolver,
8896
routeReconciler: routeReconciler,
97+
gatewayConditionUpdater: prepareGatewayConditionUpdate,
8998
}
9099
}
91100

@@ -107,6 +116,7 @@ type gatewayReconciler struct {
107116
logger logr.Logger
108117
metricsCollector lbcmetrics.MetricCollector
109118
reconcileTracker func(namespaceName types.NamespacedName)
119+
gatewayConditionUpdater func(gw *gwv1.Gateway, targetConditionType string, newStatus metav1.ConditionStatus, reason string, message string) bool
110120

111121
cfgResolver gatewayConfigResolver
112122
routeReconciler routeutils.RouteReconciler
@@ -186,12 +196,23 @@ func (r *gatewayReconciler) reconcileHelper(ctx context.Context, req reconcile.R
186196
mergedLbConfig, err := r.cfgResolver.getLoadBalancerConfigForGateway(ctx, r.k8sClient, gw, gwClass)
187197

188198
if err != nil {
199+
statusErr := r.updateGatewayStatusFailure(ctx, gw, gwv1.GatewayReasonInvalid, err)
200+
if statusErr != nil {
201+
r.logger.Error(err, "Unable to update gateway status on failure to retrieve attached config")
202+
}
189203
return err
190204
}
191205

192206
allRoutes, err := r.gatewayLoader.LoadRoutesForGateway(ctx, *gw, r.routeFilter, r.routeReconciler)
193207

194208
if err != nil {
209+
var loaderErr routeutils.LoaderError
210+
if errors.As(err, &loaderErr) {
211+
statusErr := r.updateGatewayStatusFailure(ctx, gw, loaderErr.GetReason(), loaderErr)
212+
if statusErr != nil {
213+
r.logger.Error(statusErr, "Unable to update gateway status on failure to build routes")
214+
}
215+
}
195216
return err
196217
}
197218

@@ -248,18 +269,14 @@ func (r *gatewayReconciler) reconcileUpdate(ctx context.Context, gw *gwv1.Gatewa
248269
if err != nil {
249270
return err
250271
}
251-
lbDNS, err := lb.DNSName().Resolve(ctx)
252-
if err != nil {
253-
return err
254-
}
255272

256273
if !backendSGRequired {
257274
if err := r.backendSGProvider.Release(ctx, networking.ResourceTypeGateway, []types.NamespacedName{k8s.NamespacedName(gw)}); err != nil {
258275
return err
259276
}
260277
}
261278

262-
if err = r.updateGatewayStatus(ctx, lbDNS, gw); err != nil {
279+
if err = r.updateGatewayStatusSuccess(ctx, lb.Status, gw); err != nil {
263280
r.eventRecorder.Event(gw, corev1.EventTypeWarning, k8s.GatewayEventReasonFailedUpdateStatus, fmt.Sprintf("Failed update status due to %v", err))
264281
return err
265282
}
@@ -295,28 +312,59 @@ func (r *gatewayReconciler) buildModel(ctx context.Context, gw *gwv1.Gateway, cf
295312
return stack, lb, backendSGRequired, nil
296313
}
297314

298-
func (r *gatewayReconciler) updateGatewayStatus(ctx context.Context, lbDNS string, gw *gwv1.Gateway) error {
299-
// TODO Consider LB ARN.
315+
func (r *gatewayReconciler) updateGatewayStatusSuccess(ctx context.Context, lbStatus *elbv2model.LoadBalancerStatus, gw *gwv1.Gateway) error {
316+
// LB Status should always be set, if it's not, we need to prevent NPE
317+
if lbStatus == nil {
318+
r.logger.Info("Unable to update Gateway Status due to null LB status")
319+
return nil
320+
}
321+
gwOld := gw.DeepCopy()
322+
323+
var needPatch bool
324+
var requeueNeeded bool
325+
if isGatewayProgrammed(*lbStatus) {
326+
needPatch = prepareGatewayConditionUpdate(gw, string(gwv1.GatewayConditionProgrammed), metav1.ConditionTrue, string(gwv1.GatewayConditionProgrammed), lbStatus.LoadBalancerARN)
327+
} else {
328+
requeueNeeded = true
329+
}
300330

301-
// Gateway Address Status
331+
needPatch = prepareGatewayConditionUpdate(gw, string(gwv1.GatewayConditionAccepted), metav1.ConditionTrue, string(gwv1.GatewayConditionAccepted), "") || needPatch
302332
if len(gw.Status.Addresses) != 1 ||
303333
gw.Status.Addresses[0].Value != "" ||
304-
gw.Status.Addresses[0].Value != lbDNS {
305-
gwOld := gw.DeepCopy()
334+
gw.Status.Addresses[0].Value != lbStatus.DNSName {
306335
ipAddressType := gwv1.HostnameAddressType
307336
gw.Status.Addresses = []gwv1.GatewayStatusAddress{
308337
{
309338
Type: &ipAddressType,
310-
Value: lbDNS,
339+
Value: lbStatus.DNSName,
311340
},
312341
}
342+
needPatch = true
343+
}
344+
345+
if needPatch {
313346
if err := r.k8sClient.Status().Patch(ctx, gw, client.MergeFrom(gwOld)); err != nil {
314347
return errors.Wrapf(err, "failed to update gw status: %v", k8s.NamespacedName(gw))
315348
}
316349
}
317350

318-
// TODO: Listener status ListenerStatus
319-
// https://github.com/aws/aws-application-networking-k8s/blob/main/pkg/controllers/gateway_controller.go#L350
351+
if requeueNeeded {
352+
return runtime.NewRequeueNeededAfter(requeueMessage, statusUpdateRequeueTime)
353+
}
354+
355+
return nil
356+
}
357+
358+
func (r *gatewayReconciler) updateGatewayStatusFailure(ctx context.Context, gw *gwv1.Gateway, reason gwv1.GatewayConditionReason, err error) error {
359+
gwOld := gw.DeepCopy()
360+
361+
needPatch := prepareGatewayConditionUpdate(gw, string(gwv1.GatewayConditionAccepted), metav1.ConditionFalse, string(reason), err.Error())
362+
363+
if needPatch {
364+
if err := r.k8sClient.Status().Patch(ctx, gw, client.MergeFrom(gwOld)); err != nil {
365+
return errors.Wrapf(err, "failed to update gw status: %v", k8s.NamespacedName(gw))
366+
}
367+
}
320368

321369
return nil
322370
}
@@ -475,3 +523,12 @@ func (r *gatewayReconciler) setupNLBGatewayControllerWatches(ctrl controller.Con
475523
return nil
476524

477525
}
526+
527+
func isGatewayProgrammed(lbStatus elbv2model.LoadBalancerStatus) bool {
528+
if lbStatus.ProvisioningState == nil {
529+
return false
530+
}
531+
532+
return lbStatus.ProvisioningState.Code == elbv2types.LoadBalancerStateEnumActive || lbStatus.ProvisioningState.Code == elbv2types.LoadBalancerStateEnumActiveImpaired
533+
534+
}

controllers/gateway/utils.go

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,21 @@ import (
1414
"strconv"
1515
"strings"
1616
"time"
17+
"unicode/utf8"
1718
)
1819

1920
const (
2021
gatewayClassAnnotationLastProcessedConfig = "elbv2.k8s.aws/last-processed-config"
2122
gatewayClassAnnotationLastProcessedConfigTimestamp = gatewayClassAnnotationLastProcessedConfig + "-timestamp"
23+
24+
// The max message that can be stored in a condition
25+
maxMessageLength = 32700
2226
)
2327

28+
// updateGatewayClassLastProcessedConfig updates the gateway class annotations with the last processed lb config resource version or "" if no lb config is attached to the gatewayclass
2429
func updateGatewayClassLastProcessedConfig(ctx context.Context, k8sClient client.Client, gwClass *gwv1.GatewayClass, lbConf *elbv2gw.LoadBalancerConfiguration) error {
2530

26-
calculatedVersion := gatewayClassAnnotationLastProcessedConfig
31+
calculatedVersion := ""
2732

2833
if lbConf != nil {
2934
calculatedVersion = lbConf.ResourceVersion
@@ -36,12 +41,16 @@ func updateGatewayClassLastProcessedConfig(ctx context.Context, k8sClient client
3641
}
3742

3843
gwClassOld := gwClass.DeepCopy()
44+
if gwClass.Annotations == nil {
45+
gwClass.Annotations = make(map[string]string)
46+
}
3947
gwClass.Annotations[gatewayClassAnnotationLastProcessedConfig] = calculatedVersion
4048
gwClass.Annotations[gatewayClassAnnotationLastProcessedConfigTimestamp] = strconv.FormatInt(time.Now().Unix(), 10)
4149

4250
return k8sClient.Patch(ctx, gwClass, client.MergeFrom(gwClassOld))
4351
}
4452

53+
// getStoredProcessedConfig retrieves the resource version attached to the lb config referenced by the gateway class or nil if no such mapping exists.
4554
func getStoredProcessedConfig(gwClass *gwv1.GatewayClass) *string {
4655
var storedVersion *string
4756

@@ -54,10 +63,20 @@ func getStoredProcessedConfig(gwClass *gwv1.GatewayClass) *string {
5463
return storedVersion
5564
}
5665

66+
// updateGatewayClassAcceptedCondition updates the 'accepted' condition on the gateway class to the passed in parameters. if no 'Accepted' condition exists, do nothing.
5767
func updateGatewayClassAcceptedCondition(ctx context.Context, k8sClient client.Client, gwClass *gwv1.GatewayClass, newStatus metav1.ConditionStatus, reason string, message string) error {
58-
derivedStatus, indxToUpdate := deriveGatewayClassAcceptedStatus(gwClass)
68+
indxToUpdate, ok := deriveAcceptedConditionIndex(gwClass)
69+
70+
if ok {
71+
72+
storedStatus := gwClass.Status.Conditions[indxToUpdate].Status
73+
storedMessage := gwClass.Status.Conditions[indxToUpdate].Message
74+
storedReason := gwClass.Status.Conditions[indxToUpdate].Reason
75+
76+
if storedStatus == newStatus && storedMessage == message && storedReason == reason {
77+
return nil
78+
}
5979

60-
if indxToUpdate != -1 && derivedStatus != newStatus {
6180
gwClassOld := gwClass.DeepCopy()
6281
gwClass.Status.Conditions[indxToUpdate].LastTransitionTime = metav1.NewTime(time.Now())
6382
gwClass.Status.Conditions[indxToUpdate].ObservedGeneration = gwClass.Generation
@@ -71,15 +90,56 @@ func updateGatewayClassAcceptedCondition(ctx context.Context, k8sClient client.C
7190
return nil
7291
}
7392

74-
func deriveGatewayClassAcceptedStatus(gwClass *gwv1.GatewayClass) (metav1.ConditionStatus, int) {
93+
// prepareGatewayConditionUpdate inserts the necessary data into the condition field of the gateway. The caller should patch the corresponding gateway. Returns false when no change was performed.
94+
func prepareGatewayConditionUpdate(gw *gwv1.Gateway, targetConditionType string, newStatus metav1.ConditionStatus, reason string, message string) bool {
95+
96+
indxToUpdate := -1
97+
var derivedCondition metav1.Condition
98+
for i, condition := range gw.Status.Conditions {
99+
if condition.Type == targetConditionType {
100+
indxToUpdate = i
101+
derivedCondition = condition
102+
break
103+
}
104+
}
105+
106+
// 32768 is the max message limit
107+
truncatedMessage := truncateMessage(message)
108+
109+
if indxToUpdate != -1 {
110+
if derivedCondition.Status != newStatus || derivedCondition.Message != truncatedMessage || derivedCondition.Reason != reason {
111+
gw.Status.Conditions[indxToUpdate].LastTransitionTime = metav1.NewTime(time.Now())
112+
gw.Status.Conditions[indxToUpdate].ObservedGeneration = gw.Generation
113+
gw.Status.Conditions[indxToUpdate].Status = newStatus
114+
gw.Status.Conditions[indxToUpdate].Message = truncatedMessage
115+
gw.Status.Conditions[indxToUpdate].Reason = reason
116+
return true
117+
}
118+
}
119+
return false
120+
}
121+
122+
func truncateMessage(s string) string {
123+
if utf8.RuneCountInString(s) <= maxMessageLength {
124+
return s
125+
}
126+
127+
runes := []rune(s)
128+
return string(runes[:maxMessageLength]) + "..."
129+
}
130+
131+
// deriveAcceptedConditionIndex returns the index of the condition pertaining to the accepted condition.
132+
// -1 if the condition doesn't exist
133+
func deriveAcceptedConditionIndex(gwClass *gwv1.GatewayClass) (int, bool) {
75134
for i, v := range gwClass.Status.Conditions {
76135
if v.Type == string(gwv1.GatewayClassReasonAccepted) {
77-
return v.Status, i
136+
return i, true
78137
}
79138
}
80-
return metav1.ConditionFalse, -1
139+
return -1, false
81140
}
82141

142+
// resolveLoadBalancerConfig returns the lb config referenced in the ParametersReference.
83143
func resolveLoadBalancerConfig(ctx context.Context, k8sClient client.Client, reference *gwv1.ParametersReference) (*elbv2gw.LoadBalancerConfiguration, error) {
84144
var lbConf *elbv2gw.LoadBalancerConfiguration
85145

0 commit comments

Comments
 (0)