Skip to content

Commit 5bdf196

Browse files
committed
handle cross zone disabled for alb
1 parent 9779b80 commit 5bdf196

File tree

3 files changed

+405
-7
lines changed

3 files changed

+405
-7
lines changed

pkg/annotations/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const (
8181

8282
// NLB annotation suffixes
8383
// prefixes service.beta.kubernetes.io, service.kubernetes.io
84+
SvcBetaAnnotationPrefix = "service.beta.kubernetes.io"
8485
SvcLBSuffixSourceRanges = "load-balancer-source-ranges"
8586
SvcLBSuffixLoadBalancerType = "aws-load-balancer-type"
8687
SvcLBSuffixTargetType = "aws-load-balancer-nlb-target-type"

pkg/targetgroupbinding/resource_manager.go

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,27 @@ package targetgroupbinding
33
import (
44
"context"
55
"fmt"
6-
smithy "github.com/aws/smithy-go"
7-
"k8s.io/apimachinery/pkg/util/cache"
86
"net/netip"
97
"sync"
108
"time"
119

10+
smithy "github.com/aws/smithy-go"
11+
"k8s.io/apimachinery/pkg/util/cache"
12+
13+
"strings"
14+
1215
awssdk "github.com/aws/aws-sdk-go-v2/aws"
1316
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
1417
"github.com/go-logr/logr"
1518
"github.com/pkg/errors"
1619
corev1 "k8s.io/api/core/v1"
1720
apierrors "k8s.io/apimachinery/pkg/api/errors"
1821
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
"k8s.io/apimachinery/pkg/types"
1923
"k8s.io/apimachinery/pkg/util/sets"
2024
"k8s.io/client-go/tools/record"
2125
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
26+
"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
2227
"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
2328
"sigs.k8s.io/aws-load-balancer-controller/pkg/backend"
2429
ctrlerrors "sigs.k8s.io/aws-load-balancer-controller/pkg/error"
@@ -53,6 +58,7 @@ func NewDefaultResourceManager(k8sClient client.Client, elbv2Client services.ELB
5358
endpointResolver := backend.NewDefaultEndpointResolver(k8sClient, podInfoRepo, failOpenEnabled, endpointSliceEnabled, logger)
5459
return &defaultResourceManager{
5560
k8sClient: k8sClient,
61+
elbv2Client: elbv2Client,
5662
targetsManager: targetsManager,
5763
endpointResolver: endpointResolver,
5864
networkingManager: networkingManager,
@@ -77,6 +83,7 @@ var _ ResourceManager = &defaultResourceManager{}
7783
// default implementation for ResourceManager.
7884
type defaultResourceManager struct {
7985
k8sClient client.Client
86+
elbv2Client services.ELBV2
8087
targetsManager TargetsManager
8188
endpointResolver backend.EndpointResolver
8289
networkingManager networking.NetworkingManager
@@ -569,19 +576,26 @@ func (m *defaultResourceManager) registerPodEndpoints(ctx context.Context, tgb *
569576
}
570577

571578
overrideAzFn, err := m.generateOverrideAzFn(ctx, vpcID, tgb.Spec.IamRoleArnToAssume)
579+
if err != nil {
580+
return err
581+
}
572582

583+
svcAnnotations, err := m.getServiceAnnotations(ctx, tgb)
573584
if err != nil {
574585
return err
575586
}
576587

577-
sdkTargets, err := m.prepareRegistrationCall(endpoints, tgb, overrideAzFn)
588+
sdkTargets, err := m.prepareRegistrationCall(ctx, endpoints, tgb, overrideAzFn, svcAnnotations)
578589
if err != nil {
579590
return err
580591
}
581592
return m.targetsManager.RegisterTargets(ctx, tgb, sdkTargets)
582593
}
583594

584-
func (m *defaultResourceManager) prepareRegistrationCall(endpoints []backend.PodEndpoint, tgb *elbv2api.TargetGroupBinding, doAzOverride func(addr netip.Addr) bool) ([]elbv2types.TargetDescription, error) {
595+
func (m *defaultResourceManager) prepareRegistrationCall(ctx context.Context, endpoints []backend.PodEndpoint, tgb *elbv2api.TargetGroupBinding, doAzOverride func(addr netip.Addr) bool, svcAnnotations map[string]string) ([]elbv2types.TargetDescription, error) {
596+
isALB := m.isALBIngress(svcAnnotations)
597+
crossZoneDisabled := m.isCrossZoneDisabled(svcAnnotations, isALB)
598+
585599
sdkTargets := make([]elbv2types.TargetDescription, 0, len(endpoints))
586600
for _, endpoint := range endpoints {
587601
target := elbv2types.TargetDescription{
@@ -593,7 +607,15 @@ func (m *defaultResourceManager) prepareRegistrationCall(endpoints []backend.Pod
593607
return sdkTargets, err
594608
}
595609
if doAzOverride(podIP) {
596-
target.AvailabilityZone = awssdk.String("all")
610+
if isALB && crossZoneDisabled && tgb.Spec.IamRoleArnToAssume == "" {
611+
az, err := m.getPodAvailabilityZone(ctx, endpoint.Pod)
612+
if err != nil {
613+
return sdkTargets, err
614+
}
615+
target.AvailabilityZone = awssdk.String(az)
616+
} else {
617+
target.AvailabilityZone = awssdk.String("all")
618+
}
597619
}
598620

599621
doAppend := true
@@ -844,3 +866,57 @@ func (m *defaultResourceManager) getMaxNewTargets(newTargetCount int, currentTar
844866

845867
return newTargetCount
846868
}
869+
870+
func (m *defaultResourceManager) getServiceAnnotations(ctx context.Context, tgb *elbv2api.TargetGroupBinding) (map[string]string, error) {
871+
svcKey := buildServiceReferenceKey(tgb, tgb.Spec.ServiceRef)
872+
svc := &corev1.Service{}
873+
if err := m.k8sClient.Get(ctx, svcKey, svc); err != nil {
874+
return nil, err
875+
}
876+
return svc.Annotations, nil
877+
}
878+
879+
func (m *defaultResourceManager) isALBIngress(svcAnnotations map[string]string) bool {
880+
for key := range svcAnnotations {
881+
if strings.HasPrefix(key, annotations.AnnotationPrefixIngress) {
882+
return true
883+
}
884+
}
885+
return false
886+
}
887+
888+
func (m *defaultResourceManager) isCrossZoneDisabled(svcAnnotations map[string]string, isALB bool) bool {
889+
crossZoneDisabled := "load_balancing.cross_zone.enabled=false"
890+
if isALB {
891+
if attrs, ok := svcAnnotations[annotations.AnnotationPrefixIngress+"/"+annotations.IngressSuffixTargetGroupAttributes]; ok {
892+
return strings.Contains(attrs, crossZoneDisabled)
893+
}
894+
} else {
895+
if attrs, ok := svcAnnotations[annotations.SvcBetaAnnotationPrefix+"/"+annotations.SvcLBSuffixLoadBalancerAttributes]; ok {
896+
return strings.Contains(attrs, crossZoneDisabled)
897+
}
898+
}
899+
return false
900+
}
901+
902+
func (m *defaultResourceManager) getPodAvailabilityZone(ctx context.Context, pod k8s.PodInfo) (string, error) {
903+
if pod.NodeName == "" {
904+
return "", errors.Errorf("pod %s has no node assigned", pod.Key)
905+
}
906+
907+
node := &corev1.Node{}
908+
if err := m.k8sClient.Get(ctx, types.NamespacedName{Name: pod.NodeName}, node); err != nil {
909+
return "", err
910+
}
911+
912+
az, ok := node.Labels[corev1.LabelTopologyZone]
913+
// fallback: try legacy/deprecated label failure-domain.beta.kubernetes.io/zone
914+
if !ok {
915+
az, ok = node.Labels["failure-domain.beta.kubernetes.io/zone"]
916+
}
917+
if !ok {
918+
return "", errors.Errorf("node %s has no availability zone label", pod.NodeName)
919+
}
920+
921+
return az, nil
922+
}

0 commit comments

Comments
 (0)