Skip to content

Commit a64b174

Browse files
feat: add cache for resolution of target group ARN by name
1 parent fbc137b commit a64b174

File tree

3 files changed

+107
-35
lines changed

3 files changed

+107
-35
lines changed

pkg/ingress/model_build_actions.go

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@ import (
44
"context"
55
"fmt"
66
awssdk "github.com/aws/aws-sdk-go-v2/aws"
7-
elbv2sdk "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
8-
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
97
"github.com/pkg/errors"
108
corev1 "k8s.io/api/core/v1"
119
"k8s.io/apimachinery/pkg/types"
12-
"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
1310
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
1411
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
1512
"strings"
@@ -109,11 +106,11 @@ func (t *defaultModelBuildTask) buildForwardAction(ctx context.Context, ing Clas
109106
if tgt.TargetGroupARN != nil {
110107
tgARN = core.LiteralStringToken(*tgt.TargetGroupARN)
111108
} else if tgt.TargetGroupName != nil {
112-
tgObj, err := getTargetGroupsByNameFromAWS(ctx, t.elbv2Client, tgt)
109+
targetGroupARN, err := t.targetGroupNameToArnMapper.getArnByName(ctx, *tgt.TargetGroupName)
113110
if err != nil {
114111
return elbv2model.Action{}, fmt.Errorf("searching TargetGroup with name %s: %w", *tgt.TargetGroupName, err)
115112
}
116-
tgARN = core.LiteralStringToken(*tgObj.TargetGroupArn)
113+
tgARN = core.LiteralStringToken(targetGroupARN)
117114
} else {
118115
svcKey := types.NamespacedName{
119116
Namespace: ing.Ing.Namespace,
@@ -235,19 +232,3 @@ func (t *defaultModelBuildTask) buildSSLRedirectAction(_ context.Context, sslRed
235232
},
236233
}
237234
}
238-
239-
// getTargetGroupsByNameFromAWS returns the AWS target group corresponding to the name
240-
func getTargetGroupsByNameFromAWS(ctx context.Context, elbv2Client services.ELBV2, tgt TargetGroupTuple) (*elbv2types.TargetGroup, error) {
241-
req := &elbv2sdk.DescribeTargetGroupsInput{
242-
Names: []string{*tgt.TargetGroupName},
243-
}
244-
245-
tgList, err := elbv2Client.DescribeTargetGroupsAsList(ctx, req)
246-
if err != nil {
247-
return nil, err
248-
}
249-
if len(tgList) != 1 {
250-
return nil, errors.Errorf("expecting a single targetGroup with query [%s] but got %v", *tgt.TargetGroupName, len(tgList))
251-
}
252-
return &tgList[0], nil
253-
}

pkg/ingress/model_build_actions_test.go

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -355,42 +355,72 @@ func Test_defaultModelBuildTask_buildForwardActionWithTargetGroupName(t *testing
355355
ingress ClassifiedIngress
356356
forwardActionConfig ForwardActionConfig
357357
describeTargetGroupsAsListCalls []describeTargetGroupsAsListCall
358+
cache map[string]string
358359
}
359360
tests := []struct {
360-
name string
361-
args args
362-
want elbv2model.Action
363-
wantErr error
361+
name string
362+
args args
363+
want elbv2model.Action
364+
wantErr error
365+
wantCache map[string]string
364366
}{
365367
{
366368
name: "Forward to target group identified by name",
367369
args: args{
368370
forwardActionConfig: ForwardActionConfig{
369-
TargetGroups: []TargetGroupTuple{{TargetGroupName: awssdk.String("tg-name")}},
371+
TargetGroups: []TargetGroupTuple{{TargetGroupName: awssdk.String("tg-name1")}},
370372
},
371373

372374
describeTargetGroupsAsListCalls: []describeTargetGroupsAsListCall{
373375
{
374376
req: &elbv2sdk.DescribeTargetGroupsInput{
375-
Names: []string{"tg-name"},
377+
Names: []string{"tg-name1"},
376378
},
377379
resp: []elbv2types.TargetGroup{
378380
{
379-
TargetGroupArn: awssdk.String("tg-arn"),
381+
TargetGroupArn: awssdk.String("tg-arn1"),
380382
TargetType: elbv2types.TargetTypeEnum("instance"),
381383
},
382384
},
383385
},
384386
},
387+
cache: map[string]string{},
385388
},
386389
want: elbv2model.Action{
387390
Type: elbv2model.ActionTypeForward,
388391
ForwardConfig: &elbv2model.ForwardActionConfig{
389392
TargetGroups: []elbv2model.TargetGroupTuple{{
390-
TargetGroupARN: core.LiteralStringToken("tg-arn"),
393+
TargetGroupARN: core.LiteralStringToken("tg-arn1"),
391394
}},
392395
},
393396
},
397+
wantCache: map[string]string{
398+
"tg-name1": "tg-arn1",
399+
},
400+
},
401+
{
402+
name: "Forward to target group identified by name is cached",
403+
args: args{
404+
forwardActionConfig: ForwardActionConfig{
405+
TargetGroups: []TargetGroupTuple{{TargetGroupName: awssdk.String("tg-name2")}},
406+
},
407+
408+
describeTargetGroupsAsListCalls: []describeTargetGroupsAsListCall{},
409+
cache: map[string]string{
410+
"tg-name2": "tg-arn2",
411+
},
412+
},
413+
want: elbv2model.Action{
414+
Type: elbv2model.ActionTypeForward,
415+
ForwardConfig: &elbv2model.ForwardActionConfig{
416+
TargetGroups: []elbv2model.TargetGroupTuple{{
417+
TargetGroupARN: core.LiteralStringToken("tg-arn2"),
418+
}},
419+
},
420+
},
421+
wantCache: map[string]string{
422+
"tg-name2": "tg-arn2",
423+
},
394424
},
395425
}
396426

@@ -404,14 +434,27 @@ func Test_defaultModelBuildTask_buildForwardActionWithTargetGroupName(t *testing
404434
elbv2Client.EXPECT().DescribeTargetGroupsAsList(gomock.Any(), call.req).Return(call.resp, call.err)
405435
}
406436
task := &defaultModelBuildTask{
407-
elbv2Client: elbv2Client,
437+
elbv2Client: elbv2Client,
438+
targetGroupNameToArnMapper: newTargetGroupNameToArnMapper(elbv2Client, defaultTargetGroupNameToARNCacheTTL),
439+
}
440+
441+
for targetGroupName, cachedArn := range tt.args.cache {
442+
task.targetGroupNameToArnMapper.cache.Set(targetGroupName, cachedArn, defaultTargetGroupNameToARNCacheTTL)
408443
}
444+
409445
got, err := task.buildForwardAction(context.Background(), tt.args.ingress, Action{
410446
Type: ActionTypeForward,
411447
ForwardConfig: &tt.args.forwardActionConfig,
412448
})
413449
assert.Equal(t, tt.want, got)
414450
assert.Equal(t, tt.wantErr, err)
451+
assert.Equal(t, len(tt.wantCache), task.targetGroupNameToArnMapper.cache.Len())
452+
for targetGroupName, expectedArn := range tt.wantCache {
453+
rawCacheItem, exists := task.targetGroupNameToArnMapper.cache.Get(targetGroupName)
454+
assert.True(t, exists)
455+
cachedArn := rawCacheItem.(string)
456+
assert.Equal(t, expectedArn, cachedArn)
457+
}
415458
})
416459
}
417460
}

pkg/ingress/model_builder.go

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package ingress
22

33
import (
44
"context"
5+
elbv2sdk "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
6+
"k8s.io/apimachinery/pkg/util/cache"
57
"reflect"
68
"strconv"
9+
"sync"
10+
"time"
711

812
"sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants"
913

@@ -33,7 +37,8 @@ import (
3337
)
3438

3539
const (
36-
controllerName = "ingress"
40+
controllerName = "ingress"
41+
defaultTargetGroupNameToARNCacheTTL = 20 * time.Minute
3742
)
3843

3944
// ModelBuilder is responsible for build mode stack for a IngressGroup.
@@ -80,6 +85,7 @@ func NewDefaultModelBuilder(k8sClient client.Client, eventRecorder record.EventR
8085
enableManageBackendSGRules: defaultEnableManageBackendSGRules,
8186
disableRestrictedSGRules: disableRestrictedSGRules,
8287
enableIPTargetType: enableIPTargetType,
88+
targetGroupNameToArnMapper: newTargetGroupNameToArnMapper(elbv2Client, defaultTargetGroupNameToARNCacheTTL),
8389
logger: logger,
8490
metricsCollector: metricsCollector,
8591
}
@@ -117,6 +123,7 @@ type defaultModelBuilder struct {
117123
enableManageBackendSGRules bool
118124
disableRestrictedSGRules bool
119125
enableIPTargetType bool
126+
targetGroupNameToArnMapper *targetGroupNameToArnMapper
120127

121128
logger logr.Logger
122129
metricsCollector lbcmetrics.MetricCollector
@@ -173,10 +180,11 @@ func (b *defaultModelBuilder) Build(ctx context.Context, ingGroup Group, metrics
173180
defaultHealthCheckMatcherHTTPCode: "200",
174181
defaultHealthCheckMatcherGRPCCode: "12",
175182

176-
loadBalancer: nil,
177-
frontendNlb: nil,
178-
tgByResID: make(map[string]*elbv2model.TargetGroup),
179-
backendServices: make(map[types.NamespacedName]*corev1.Service),
183+
loadBalancer: nil,
184+
frontendNlb: nil,
185+
tgByResID: make(map[string]*elbv2model.TargetGroup),
186+
backendServices: make(map[types.NamespacedName]*corev1.Service),
187+
targetGroupNameToArnMapper: b.targetGroupNameToArnMapper,
180188
}
181189
if err := task.run(ctx); err != nil {
182190
return nil, nil, nil, false, nil, nil, err
@@ -238,6 +246,7 @@ type defaultModelBuildTask struct {
238246
secretKeys []types.NamespacedName
239247
frontendNlb *elbv2model.LoadBalancer
240248
frontendNlbTargetGroupDesiredState *core.FrontendNlbTargetGroupDesiredState
249+
targetGroupNameToArnMapper *targetGroupNameToArnMapper
241250

242251
metricsCollector lbcmetrics.MetricCollector
243252
}
@@ -521,3 +530,42 @@ type listenPortConfigWithIngress struct {
521530
ingKey types.NamespacedName
522531
listenPortConfig listenPortConfig
523532
}
533+
534+
type targetGroupNameToArnMapper struct {
535+
elbv2Client services.ELBV2
536+
cache *cache.Expiring
537+
cacheTTL time.Duration
538+
cacheMutex sync.RWMutex
539+
}
540+
541+
func newTargetGroupNameToArnMapper(elbv2Client services.ELBV2, ttl time.Duration) *targetGroupNameToArnMapper {
542+
return &targetGroupNameToArnMapper{
543+
elbv2Client: elbv2Client,
544+
cache: cache.NewExpiring(),
545+
cacheTTL: ttl,
546+
}
547+
}
548+
549+
// getArnByName returns the ARN of an AWS target group identified by its name
550+
func (t *targetGroupNameToArnMapper) getArnByName(ctx context.Context, targetGroupName string) (string, error) {
551+
t.cacheMutex.Lock()
552+
defer t.cacheMutex.Unlock()
553+
554+
if rawCacheItem, exists := t.cache.Get(targetGroupName); exists {
555+
return rawCacheItem.(string), nil
556+
}
557+
req := &elbv2sdk.DescribeTargetGroupsInput{
558+
Names: []string{targetGroupName},
559+
}
560+
561+
targetGroups, err := t.elbv2Client.DescribeTargetGroupsAsList(ctx, req)
562+
if err != nil {
563+
return "", err
564+
}
565+
if len(targetGroups) != 1 {
566+
return "", errors.Errorf("expecting a single targetGroup with query [%s] but got %v", targetGroupName, len(targetGroups))
567+
}
568+
arn := *targetGroups[0].TargetGroupArn
569+
t.cache.Set(targetGroupName, arn, t.cacheTTL)
570+
return arn, nil
571+
}

0 commit comments

Comments
 (0)