Skip to content

Commit b4f3959

Browse files
committed
Enable frontend NLB
1 parent d0df42b commit b4f3959

12 files changed

+1793
-30
lines changed

Diff for: controllers/ingress/group_controller.go

+28-14
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func (r *groupReconciler) reconcile(ctx context.Context, req reconcile.Request)
151151
return errmetrics.NewErrorWithMetrics(controllerName, "add_group_finalizer_error", err, r.metricsCollector)
152152
}
153153

154-
_, lb, err := r.buildAndDeployModel(ctx, ingGroup)
154+
_, lb, frontendNlb, err := r.buildAndDeployModel(ctx, ingGroup)
155155
if err != nil {
156156
return err
157157
}
@@ -164,7 +164,14 @@ func (r *groupReconciler) reconcile(ctx context.Context, req reconcile.Request)
164164
if statusErr != nil {
165165
return
166166
}
167-
statusErr = r.updateIngressGroupStatus(ctx, ingGroup, lbDNS)
167+
var frontendNlbDNS string
168+
if frontendNlb != nil {
169+
frontendNlbDNS, statusErr = frontendNlb.DNSName().Resolve(ctx)
170+
if statusErr != nil {
171+
return
172+
}
173+
}
174+
statusErr = r.updateIngressGroupStatus(ctx, ingGroup, lbDNS, frontendNlbDNS)
168175
if statusErr != nil {
169176
r.recordIngressGroupEvent(ctx, ingGroup, corev1.EventTypeWarning, k8s.IngressEventReasonFailedUpdateStatus,
170177
fmt.Sprintf("Failed update status due to %v", statusErr))
@@ -191,38 +198,40 @@ func (r *groupReconciler) reconcile(ctx context.Context, req reconcile.Request)
191198
return nil
192199
}
193200

194-
func (r *groupReconciler) buildAndDeployModel(ctx context.Context, ingGroup ingress.Group) (core.Stack, *elbv2model.LoadBalancer, error) {
201+
func (r *groupReconciler) buildAndDeployModel(ctx context.Context, ingGroup ingress.Group) (core.Stack, *elbv2model.LoadBalancer, *elbv2model.LoadBalancer, error) {
195202
var stack core.Stack
196203
var lb *elbv2model.LoadBalancer
197204
var secrets []types.NamespacedName
198205
var backendSGRequired bool
199206
var err error
207+
var frontendNlbTargetGroupDesiredState *core.FrontendNlbTargetGroupDesiredState
208+
var frontendNlb *elbv2model.LoadBalancer
200209
buildModelFn := func() {
201-
stack, lb, secrets, backendSGRequired, err = r.modelBuilder.Build(ctx, ingGroup, r.metricsCollector)
210+
stack, lb, secrets, backendSGRequired, frontendNlbTargetGroupDesiredState, frontendNlb, err = r.modelBuilder.Build(ctx, ingGroup, r.metricsCollector)
202211
}
203212
r.metricsCollector.ObserveControllerReconcileLatency(controllerName, "build_model", buildModelFn)
204213
if err != nil {
205214
r.recordIngressGroupEvent(ctx, ingGroup, corev1.EventTypeWarning, k8s.IngressEventReasonFailedBuildModel, fmt.Sprintf("Failed build model due to %v", err))
206-
return nil, nil, errmetrics.NewErrorWithMetrics(controllerName, "build_model_error", err, r.metricsCollector)
215+
return nil, nil, nil, errmetrics.NewErrorWithMetrics(controllerName, "build_model_error", err, r.metricsCollector)
207216
}
208217
stackJSON, err := r.stackMarshaller.Marshal(stack)
209218
if err != nil {
210219
r.recordIngressGroupEvent(ctx, ingGroup, corev1.EventTypeWarning, k8s.IngressEventReasonFailedBuildModel, fmt.Sprintf("Failed build model due to %v", err))
211-
return nil, nil, err
220+
return nil, nil, nil, err
212221
}
213222
r.logger.Info("successfully built model", "model", stackJSON)
214223

215224
deployModelFn := func() {
216-
err = r.stackDeployer.Deploy(ctx, stack, r.metricsCollector, "ingress")
225+
err = r.stackDeployer.Deploy(ctx, stack, r.metricsCollector, "ingress", frontendNlbTargetGroupDesiredState)
217226
}
218227
r.metricsCollector.ObserveControllerReconcileLatency(controllerName, "deploy_model", deployModelFn)
219228
if err != nil {
220229
var requeueNeededAfter *runtime.RequeueNeededAfter
221230
if errors.As(err, &requeueNeededAfter) {
222-
return nil, nil, err
231+
return nil, nil, nil, err
223232
}
224233
r.recordIngressGroupEvent(ctx, ingGroup, corev1.EventTypeWarning, k8s.IngressEventReasonFailedDeployModel, fmt.Sprintf("Failed deploy model due to %v", err))
225-
return nil, nil, errmetrics.NewErrorWithMetrics(controllerName, "deploy_model_error", err, r.metricsCollector)
234+
return nil, nil, nil, errmetrics.NewErrorWithMetrics(controllerName, "deploy_model_error", err, r.metricsCollector)
226235
}
227236
r.logger.Info("successfully deployed model", "ingressGroup", ingGroup.ID)
228237
r.secretsManager.MonitorSecrets(ingGroup.ID.String(), secrets)
@@ -232,9 +241,9 @@ func (r *groupReconciler) buildAndDeployModel(ctx context.Context, ingGroup ingr
232241
inactiveResources = append(inactiveResources, k8s.ToSliceOfNamespacedNames(ingGroup.Members)...)
233242
}
234243
if err := r.backendSGProvider.Release(ctx, networkingpkg.ResourceTypeIngress, inactiveResources); err != nil {
235-
return nil, nil, errmetrics.NewErrorWithMetrics(controllerName, "release_auto_generated_backend_sg_error", err, r.metricsCollector)
244+
return nil, nil, nil, errmetrics.NewErrorWithMetrics(controllerName, "release_auto_generated_backend_sg_error", err, r.metricsCollector)
236245
}
237-
return stack, lb, nil
246+
return stack, lb, frontendNlb, nil
238247
}
239248

240249
func (r *groupReconciler) recordIngressGroupEvent(_ context.Context, ingGroup ingress.Group, eventType string, reason string, message string) {
@@ -243,16 +252,16 @@ func (r *groupReconciler) recordIngressGroupEvent(_ context.Context, ingGroup in
243252
}
244253
}
245254

246-
func (r *groupReconciler) updateIngressGroupStatus(ctx context.Context, ingGroup ingress.Group, lbDNS string) error {
255+
func (r *groupReconciler) updateIngressGroupStatus(ctx context.Context, ingGroup ingress.Group, lbDNS string, frontendNLBDNS string) error {
247256
for _, member := range ingGroup.Members {
248-
if err := r.updateIngressStatus(ctx, lbDNS, member.Ing); err != nil {
257+
if err := r.updateIngressStatus(ctx, lbDNS, frontendNLBDNS, member.Ing); err != nil {
249258
return err
250259
}
251260
}
252261
return nil
253262
}
254263

255-
func (r *groupReconciler) updateIngressStatus(ctx context.Context, lbDNS string, ing *networking.Ingress) error {
264+
func (r *groupReconciler) updateIngressStatus(ctx context.Context, lbDNS string, frontendNLBDNS string, ing *networking.Ingress) error {
256265
if len(ing.Status.LoadBalancer.Ingress) != 1 ||
257266
ing.Status.LoadBalancer.Ingress[0].IP != "" ||
258267
ing.Status.LoadBalancer.Ingress[0].Hostname != lbDNS {
@@ -262,6 +271,11 @@ func (r *groupReconciler) updateIngressStatus(ctx context.Context, lbDNS string,
262271
Hostname: lbDNS,
263272
},
264273
}
274+
if frontendNLBDNS != "" {
275+
ing.Status.LoadBalancer.Ingress = append(ing.Status.LoadBalancer.Ingress, networking.IngressLoadBalancerIngress{
276+
Hostname: frontendNLBDNS,
277+
})
278+
}
265279
if err := r.k8sClient.Status().Patch(ctx, ing, client.MergeFrom(ingOld)); err != nil {
266280
return errors.Wrapf(err, "failed to update ingress status: %v", k8s.NamespacedName(ing))
267281
}

Diff for: controllers/service/service_controller.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ func (r *serviceReconciler) buildModel(ctx context.Context, svc *corev1.Service)
152152
}
153153

154154
func (r *serviceReconciler) deployModel(ctx context.Context, svc *corev1.Service, stack core.Stack) error {
155-
if err := r.stackDeployer.Deploy(ctx, stack, r.metricsCollector, "service"); err != nil {
155+
albTargetGroupDesiredState := core.NewFrontendNlbTargetGroupDesiredState()
156+
if err := r.stackDeployer.Deploy(ctx, stack, r.metricsCollector, "service", albTargetGroupDesiredState); err != nil {
156157
var requeueNeededAfter *runtime.RequeueNeededAfter
157158
if errors.As(err, &requeueNeededAfter) {
158159
return err

Diff for: pkg/annotations/constants.go

+13
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ const (
5959
IngressLBSuffixMultiClusterTargetGroup = "multi-cluster-target-group"
6060
IngressSuffixLoadBalancerCapacityReservation = "minimum-load-balancer-capacity"
6161
IngressSuffixIPAMIPv4PoolId = "ipam-ipv4-pool-id"
62+
IngressSuffixEnableFrontendNlb = "enable-frontend-nlb"
63+
IngressSuffixFrontendNlbScheme = "frontend-nlb-scheme"
64+
IngressSuffixFrontendNlbSubnets = "frontend-nlb-subnets"
65+
IngressSuffixFrontendNlbSecurityGroups = "frontend-nlb-security-groups"
66+
IngressSuffixFrontendNlbListenerPortMapping = "frontend-nlb-listener-port-mapping"
67+
IngressSuffixFrontendNlbHealthCheckPort = "frontend-nlb-healthcheck-port"
68+
IngressSuffixFrontendNlbHealthCheckProtocol = "frontend-nlb-healthcheck-protocol"
69+
IngressSuffixFrontendNlbHealthCheckPath = "frontend-nlb-healthcheck-path"
70+
IngressSuffixFrontendNlbHealthCheckIntervalSeconds = "frontend-nlb-healthcheck-interval-seconds"
71+
IngressSuffixFrontendNlbHealthCheckTimeoutSeconds = "frontend-nlb-healthcheck-timeout-seconds"
72+
IngressSuffixFrontendNlbHealthCheckHealthyThresholdCount = "frontend-nlb-healthcheck-healthy-threshold-count"
73+
IngressSuffixFrontendNlHealthCheckbUnhealthyThresholdCount = "frontend-nlb-healthcheck-unhealthy-threshold-count"
74+
IngressSuffixFrontendNlbHealthCheckSuccessCodes = "frontend-nlb-healthcheck-success-codes"
6275

6376
// NLB annotation suffixes
6477
// prefixes service.beta.kubernetes.io, service.kubernetes.io

Diff for: pkg/deploy/elbv2/frontend_nlb_target_synthesizer.go

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package elbv2
2+
3+
import (
4+
"context"
5+
6+
awssdk "github.com/aws/aws-sdk-go-v2/aws"
7+
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
8+
"github.com/go-logr/logr"
9+
"github.com/pkg/errors"
10+
"sigs.k8s.io/aws-load-balancer-controller/pkg/config"
11+
"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
12+
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
13+
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
15+
)
16+
17+
func NewFrontendNlbTargetSynthesizer(k8sClient client.Client, trackingProvider tracking.Provider, taggingManager TaggingManager, frontendNlbTargetsManager FrontendNlbTargetsManager, logger logr.Logger, featureGates config.FeatureGates, stack core.Stack, frontendNlbTargetGroupDesiredState *core.FrontendNlbTargetGroupDesiredState) *frontendNlbTargetSynthesizer {
18+
return &frontendNlbTargetSynthesizer{
19+
k8sClient: k8sClient,
20+
trackingProvider: trackingProvider,
21+
taggingManager: taggingManager,
22+
frontendNlbTargetsManager: frontendNlbTargetsManager,
23+
featureGates: featureGates,
24+
logger: logger,
25+
stack: stack,
26+
frontendNlbTargetGroupDesiredState: frontendNlbTargetGroupDesiredState,
27+
unmatchedResTGs: nil,
28+
}
29+
}
30+
31+
type frontendNlbTargetSynthesizer struct {
32+
k8sClient client.Client
33+
trackingProvider tracking.Provider
34+
taggingManager TaggingManager
35+
frontendNlbTargetsManager FrontendNlbTargetsManager
36+
featureGates config.FeatureGates
37+
logger logr.Logger
38+
stack core.Stack
39+
frontendNlbTargetGroupDesiredState *core.FrontendNlbTargetGroupDesiredState
40+
unmatchedResTGs []*elbv2model.TargetGroup
41+
}
42+
43+
// Synthesize processes AWS target groups and deregisters ALB targets based on the desired state.
44+
func (s *frontendNlbTargetSynthesizer) Synthesize(ctx context.Context) error {
45+
var resTGs []*elbv2model.TargetGroup
46+
s.stack.ListResources(&resTGs)
47+
sdkTGs, err := s.findSDKTargetGroups(ctx)
48+
if err != nil {
49+
return err
50+
}
51+
_, _, unmatchedSDKTGs, err := matchResAndSDKTargetGroups(resTGs, sdkTGs,
52+
s.trackingProvider.ResourceIDTagKey(), s.featureGates)
53+
if err != nil {
54+
return err
55+
}
56+
57+
for _, sdkTG := range unmatchedSDKTGs {
58+
if sdkTG.TargetGroup.TargetType != "alb" {
59+
continue
60+
}
61+
62+
err := s.deregisterCurrentTarget(ctx, sdkTG)
63+
if err != nil {
64+
return errors.Wrapf(err, "failed to deregister target for the target group: %s", *sdkTG.TargetGroup.TargetGroupArn)
65+
}
66+
}
67+
68+
return nil
69+
70+
}
71+
72+
func (s *frontendNlbTargetSynthesizer) deregisterCurrentTarget(ctx context.Context, sdkTG TargetGroupWithTags) error {
73+
// Retrieve the current targets for the target group
74+
currentTargets, err := s.frontendNlbTargetsManager.ListTargets(ctx, *sdkTG.TargetGroup.TargetGroupArn)
75+
if err != nil {
76+
return errors.Wrapf(err, "failed to list current target for target group: %s", *sdkTG.TargetGroup.TargetGroupArn)
77+
}
78+
79+
// If there is no target, nothing to deregister
80+
if len(currentTargets) == 0 {
81+
s.logger.Info("No targets to deregister",
82+
"targetGroupARN", *sdkTG.TargetGroup.TargetGroupArn)
83+
return nil
84+
}
85+
86+
// Deregister current target
87+
s.logger.Info("Deregistering current target",
88+
"targetGroupARN", *sdkTG.TargetGroup.TargetGroupArn,
89+
"target", currentTargets[0].Target.Id,
90+
"port", currentTargets[0].Target.Port,
91+
)
92+
93+
err = s.frontendNlbTargetsManager.DeregisterTargets(ctx, *sdkTG.TargetGroup.TargetGroupArn, elbv2types.TargetDescription{
94+
Id: awssdk.String(*currentTargets[0].Target.Id),
95+
Port: awssdk.Int32(*currentTargets[0].Target.Port),
96+
})
97+
98+
if err != nil {
99+
return errors.Wrapf(err, "failed to deregister targets for target group: %s", *sdkTG.TargetGroup.TargetGroupArn)
100+
}
101+
102+
return nil
103+
}
104+
105+
func (s *frontendNlbTargetSynthesizer) PostSynthesize(ctx context.Context) error {
106+
var resTGs []*elbv2model.TargetGroup
107+
s.stack.ListResources(&resTGs)
108+
109+
// Filter desired target group to include only ALB type target group
110+
albResTGs := filterALBTargetGroups(resTGs)
111+
112+
for _, resTG := range albResTGs {
113+
114+
// Skip target group that are not yet created
115+
if resTG.Status.TargetGroupARN == "" {
116+
continue
117+
}
118+
119+
// List current targets
120+
currentTargets, err := s.frontendNlbTargetsManager.ListTargets(ctx, resTG.Status.TargetGroupARN)
121+
122+
if err != nil {
123+
return err
124+
}
125+
126+
desiredTarget, err := s.frontendNlbTargetGroupDesiredState.TargetGroups[resTG.Spec.Name].TargetARN.Resolve(ctx)
127+
desiredTargetPort := s.frontendNlbTargetGroupDesiredState.TargetGroups[resTG.Spec.Name].TargetPort
128+
129+
if err != nil {
130+
return errors.Wrapf(err, "failed to resolve the desiredTarget for target group: %s", desiredTarget)
131+
}
132+
133+
if len(currentTargets) == 0 ||
134+
currentTargets[0].Target == nil ||
135+
currentTargets[0].Target.Id == nil ||
136+
*currentTargets[0].Target.Id != desiredTarget {
137+
err = s.frontendNlbTargetsManager.RegisterTargets(ctx, resTG.Status.TargetGroupARN, elbv2types.TargetDescription{
138+
Id: awssdk.String(desiredTarget),
139+
Port: awssdk.Int32(desiredTargetPort),
140+
})
141+
142+
if err != nil {
143+
return err
144+
}
145+
146+
}
147+
148+
}
149+
150+
return nil
151+
}
152+
153+
func filterALBTargetGroups(targetGroups []*elbv2model.TargetGroup) []*elbv2model.TargetGroup {
154+
var filteredTargetGroups []*elbv2model.TargetGroup
155+
for _, tg := range targetGroups {
156+
if tg.Spec.TargetType == "alb" {
157+
filteredTargetGroups = append(filteredTargetGroups, tg)
158+
}
159+
}
160+
return filteredTargetGroups
161+
}
162+
163+
func (s *frontendNlbTargetSynthesizer) findSDKTargetGroups(ctx context.Context) ([]TargetGroupWithTags, error) {
164+
stackTags := s.trackingProvider.StackTags(s.stack)
165+
stackTagsLegacy := s.trackingProvider.StackTagsLegacy(s.stack)
166+
return s.taggingManager.ListTargetGroups(ctx,
167+
tracking.TagsAsTagFilter(stackTags),
168+
tracking.TagsAsTagFilter(stackTagsLegacy))
169+
}

0 commit comments

Comments
 (0)