Skip to content

Commit d2e817a

Browse files
committed
e2e: export both GetLoadBalancerFromDNSNameWithRetry and WithTImeout
1 parent 0948cef commit d2e817a

3 files changed

Lines changed: 146 additions & 235 deletions

File tree

tests/e2e/helper_aws.go

Lines changed: 61 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2025 The Kubernetes Authors.
2+
Copyright 2026 The Kubernetes Authors.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -30,21 +30,23 @@ import (
3030
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
3131

3232
"k8s.io/apimachinery/pkg/util/wait"
33-
clientset "k8s.io/client-go/kubernetes"
3433
"k8s.io/kubernetes/test/e2e/framework"
3534
)
3635

37-
// awsHelper provides AWS API operations for e2e tests
38-
type E2ETestHelperAWS struct {
39-
ctx context.Context
36+
const (
37+
DefaultRetryTimeoutMinutes = 20
38+
)
4039

40+
// E2ETestHelperAWS provides AWS API operations for e2e tests.
41+
type E2ETestHelperAWS struct {
42+
retryer *retry.Standard
4143
ec2Client *ec2.Client
4244
elbClient *elb.Client
4345
elbv2Client *elbv2.Client
4446
}
4547

4648
// NewAWSHelper creates a new AWS helper with configured clients
47-
func NewAWSHelper(ctx context.Context, cs clientset.Interface) (*E2ETestHelperAWS, error) {
49+
func NewAWSHelper(ctx context.Context) (*E2ETestHelperAWS, error) {
4850
cfg, err := config.LoadDefaultConfig(ctx)
4951
framework.ExpectNoError(err, "unable to load AWS config")
5052

@@ -58,198 +60,102 @@ func NewAWSHelper(ctx context.Context, cs clientset.Interface) (*E2ETestHelperAW
5860

5961
// Create AWS clients with custom retryer
6062
h := &E2ETestHelperAWS{
61-
ctx: ctx,
63+
retryer: customRetryer,
6264
ec2Client: ec2.NewFromConfig(cfg, func(o *ec2.Options) { o.Retryer = customRetryer }),
6365
elbClient: elb.NewFromConfig(cfg, func(o *elb.Options) { o.Retryer = customRetryer }),
6466
elbv2Client: elbv2.NewFromConfig(cfg, func(o *elbv2.Options) { o.Retryer = customRetryer }),
6567
}
6668

67-
// framework.Logf("Discovering cluster tag")
68-
// framework.ExpectNoError(h.discoverClusterTag(cs), "unable to find cluster tag")
69-
// // framework.Logf("Cluster tag discovered: %s", h.clusterTag)
70-
7169
return h, nil
7270
}
7371

72+
// GetEC2Client returns the EC2 client.
7473
func (h *E2ETestHelperAWS) GetEC2Client() *ec2.Client {
7574
return h.ec2Client
7675
}
7776

77+
// GetELBV2Client returns the ELBV2 client.
7878
func (h *E2ETestHelperAWS) GetELBV2Client() *elbv2.Client {
7979
return h.elbv2Client
8080
}
8181

82-
// // discoverClusterTag discovers the cluster tag from a cluster.
83-
// // The discover is done by looking up the EC2 instance tags with tag:Name prefix kubernetes.io/cluster.
84-
// // The EC2 Instance ID is discovered from a cluster node object.
85-
// // The cluster ID, VPC ID and cluster tag are discovered from the EC2 instance tags.
86-
// // If is any error is found, the function returns an error.
87-
// func (h *E2ETestHelperAWS) discoverClusterTag(cs clientset.Interface) error {
88-
// nodes, err := cs.CoreV1().Nodes().List(h.ctx, metav1.ListOptions{})
89-
// if err != nil {
90-
// return fmt.Errorf("failed to list nodes: %v", err)
91-
// }
92-
93-
// var instanceID string
94-
// for _, node := range nodes.Items {
95-
// providerID := node.Spec.ProviderID
96-
// if providerID == "" {
97-
// continue
98-
// }
99-
// providerID = strings.Replace(providerID, "aws:///", "", 1)
100-
// if len(strings.Split(providerID, "/")) < 2 {
101-
// continue
102-
// }
103-
// // h.awsRegion = strings.Split(providerID, "/")[0]
104-
// instanceID = strings.Split(providerID, "/")[1]
105-
// if !strings.HasPrefix(instanceID, "i-") {
106-
// continue
107-
// }
108-
// break
109-
// }
110-
111-
// instance, err := h.ec2Client.DescribeInstances(h.ctx, &ec2.DescribeInstancesInput{
112-
// InstanceIds: []string{instanceID},
113-
// })
114-
// if err != nil {
115-
// return fmt.Errorf("failed to describe instances: %v", err)
116-
// }
117-
118-
// clusterTagFound := false
119-
// for _, reservation := range instance.Reservations {
120-
// for _, tag := range reservation.Instances[0].Tags {
121-
// if strings.HasPrefix(aws.ToString(tag.Key), "kubernetes.io/cluster") {
122-
// // h.clusterTag = aws.ToString(tag.Key)
123-
// // h.clusterTagValue = aws.ToString(tag.Value)
124-
// clusterTagFound = true
125-
// break
126-
// }
127-
// }
128-
// if clusterTagFound {
129-
// break
130-
// }
131-
// }
132-
133-
// if !clusterTagFound {
134-
// return fmt.Errorf("cluster tag not found in the instance %s", instanceID)
135-
// }
136-
137-
// // h.clusterName = strings.Split(h.clusterTag, "/")[2]
138-
// // if h.clusterName == "" {
139-
// // return fmt.Errorf("cluster name not found in the cluster tag %s", h.clusterTag)
140-
// // }
141-
142-
// // extract VPC ID from the Instance
143-
// // for _, networkInterface := range instance.Reservations[0].Instances[0].NetworkInterfaces {
144-
// // h.vpcID = aws.ToString(networkInterface.VpcId)
145-
// // break
146-
// // }
147-
148-
// // if h.vpcID == "" {
149-
// // return fmt.Errorf("VPC ID not found in the instance %s", instanceID)
150-
// // }
151-
152-
// return nil
153-
// }
154-
155-
// GetLBTargetCount verifies the number of registered targets for a given LBv2 DNS name matches the expected count.
156-
// The steps includes:
157-
// - Get Load Balancer ARN from DNS name extracted from service Status.LoadBalancer.Ingress[0].Hostname
158-
// - List listeners for the load balancer
159-
// - Get target groups attached to listeners
160-
// - Count registered targets in target groups
161-
// - Verify count matches number of worker nodes
162-
func (h *E2ETestHelperAWS) GetLBTargetCount(lbDNSName string, expectedTargets int) error {
163-
// Get Load Balancer ARN from DNS name
164-
foundLB, err := h.GetLoadBalancerFromDNSNameWithRetry(lbDNSName, 10*time.Minute)
82+
// GetLBTargets returns the targets for a given LB DNS name, listener port, and target port.
83+
func (h *E2ETestHelperAWS) GetLBTargets(ctx context.Context, lbDNSName string, listenerPort, targetPort int32) ([]string, error) {
84+
foundLB, err := h.GetLoadBalancerFromDNSNameWithRetry(ctx, lbDNSName)
16585
if err != nil {
166-
return fmt.Errorf("failed to get load balancer from DNS name: %v", err)
86+
return nil, fmt.Errorf("failed to get load balancer from DNS name: %v", err)
16787
}
16888
lbARN := aws.ToString(foundLB.LoadBalancerArn)
16989

170-
// List listeners for the load balancer
171-
listenersOut, err := h.elbv2Client.DescribeListeners(h.ctx, &elbv2.DescribeListenersInput{
90+
listenersOut, err := h.elbv2Client.DescribeListeners(ctx, &elbv2.DescribeListenersInput{
17291
LoadBalancerArn: aws.String(lbARN),
17392
})
17493
if err != nil {
175-
return fmt.Errorf("failed to describe listeners: %v", err)
94+
return nil, fmt.Errorf("failed to describe listeners: %v", err)
17695
}
17796

178-
// Get target groups attached to listeners
17997
targetGroupARNs := map[string]struct{}{}
18098
for _, listener := range listenersOut.Listeners {
181-
if len(targetGroupARNs) > 0 {
182-
break
183-
}
184-
for _, action := range listener.DefaultActions {
185-
if action.TargetGroupArn != nil {
186-
targetGroupARNs[aws.ToString(action.TargetGroupArn)] = struct{}{}
187-
break
99+
if aws.ToInt32(listener.Port) == int32(listenerPort) {
100+
for _, action := range listener.DefaultActions {
101+
if action.TargetGroupArn != nil {
102+
targetGroupARNs[aws.ToString(action.TargetGroupArn)] = struct{}{}
103+
break
104+
}
188105
}
189106
}
190107
}
191108
if len(targetGroupARNs) == 0 {
192-
return fmt.Errorf("no target groups found for LB: %s", lbARN)
109+
return nil, fmt.Errorf("no target groups found for LB: %s", lbARN)
193110
}
194111

195-
// Count registered targets in target groups
196-
totalTargets := 0
112+
targets := []string{}
197113
for tgARN := range targetGroupARNs {
198-
tgHealth, err := h.elbv2Client.DescribeTargetHealth(h.ctx, &elbv2.DescribeTargetHealthInput{
114+
tgHealth, err := h.elbv2Client.DescribeTargetHealth(ctx, &elbv2.DescribeTargetHealthInput{
199115
TargetGroupArn: aws.String(tgARN),
200116
})
201117
if err != nil {
202-
return fmt.Errorf("failed to describe target health for TG %s: %v", tgARN, err)
118+
return nil, fmt.Errorf("failed to describe target health for TG %s: %v", tgARN, err)
119+
}
120+
for _, target := range tgHealth.TargetHealthDescriptions {
121+
if aws.ToInt32(target.Target.Port) == int32(targetPort) {
122+
targets = append(targets, aws.ToString(target.Target.Id))
123+
}
203124
}
204-
totalTargets += len(tgHealth.TargetHealthDescriptions)
205-
}
206-
207-
// Verify count matches number of worker nodes
208-
if totalTargets != expectedTargets {
209-
return fmt.Errorf("target count mismatch: expected %d, got %d", expectedTargets, totalTargets)
210125
}
211-
return nil
126+
return targets, nil
212127
}
213128

214129
// GetLoadBalancerFromDNSName describes a load balancers filtered by DNS name.
215-
func (h *E2ETestHelperAWS) GetLoadBalancerFromDNSName(lbDNSName string) (*elbv2types.LoadBalancer, error) {
216-
var foundLB *elbv2types.LoadBalancer
130+
func (h *E2ETestHelperAWS) GetLoadBalancerFromDNSName(ctx context.Context, lbDNSName string) (*elbv2types.LoadBalancer, error) {
217131
framework.Logf("describing load balancers with DNS %s", lbDNSName)
218132

219133
paginator := elbv2.NewDescribeLoadBalancersPaginator(h.elbv2Client, &elbv2.DescribeLoadBalancersInput{})
220134
for paginator.HasMorePages() {
221-
page, err := paginator.NextPage(h.ctx)
135+
page, err := paginator.NextPage(ctx)
222136
if err != nil {
223-
return nil, fmt.Errorf("failed to describe load balancers: %v", err)
137+
return nil, fmt.Errorf("failed to describe load balancers: %w", err)
224138
}
225139

226140
framework.Logf("found %d load balancers in page", len(page.LoadBalancers))
227141
// Search for the load balancer with matching DNS name in this page
228-
for i := range page.LoadBalancers {
229-
if aws.ToString(page.LoadBalancers[i].DNSName) == lbDNSName {
230-
foundLB = &page.LoadBalancers[i]
231-
framework.Logf("found load balancer with DNS %s", aws.ToString(foundLB.DNSName))
232-
break
142+
for _, lb := range page.LoadBalancers {
143+
if aws.ToString(lb.DNSName) == lbDNSName {
144+
framework.Logf("found load balancer with DNS %s", aws.ToString(lb.DNSName))
145+
return &lb, nil
233146
}
234147
}
235-
if foundLB != nil {
236-
break
237-
}
238-
}
239-
240-
if foundLB == nil {
241-
return nil, fmt.Errorf("no load balancer found with DNS name: %s", lbDNSName)
242148
}
243-
244-
return foundLB, nil
149+
return nil, fmt.Errorf("no load balancer found with DNS name: %s", lbDNSName)
245150
}
246151

247-
// GetLoadBalancerFromDNSNameWithRetry describes a load balancers filtered by DNS name with retry using
152+
// GetLoadBalancerFromDNSNameWithTimeout describes a load balancers filtered by DNS name with retry using
248153
// exponential backoff.
249-
func (h *E2ETestHelperAWS) GetLoadBalancerFromDNSNameWithRetry(lbDNSName string, timeout time.Duration) (*elbv2types.LoadBalancer, error) {
154+
// AWS API
155+
func (h *E2ETestHelperAWS) GetLoadBalancerFromDNSNameWithTimeout(ctx context.Context, lbDNSName string, timeout time.Duration) (*elbv2types.LoadBalancer, error) {
250156
var foundLB *elbv2types.LoadBalancer
251157

252-
ctx, cancel := context.WithTimeout(h.ctx, timeout)
158+
ctx, cancel := context.WithTimeout(ctx, timeout)
253159
defer cancel()
254160

255161
backoff := wait.Backoff{
@@ -262,16 +168,29 @@ func (h *E2ETestHelperAWS) GetLoadBalancerFromDNSNameWithRetry(lbDNSName string,
262168

263169
err := wait.ExponentialBackoffWithContext(ctx, backoff, func(ctx context.Context) (bool, error) {
264170
var err error
265-
foundLB, err = h.GetLoadBalancerFromDNSName(lbDNSName)
266-
if err != nil {
171+
foundLB, err = h.GetLoadBalancerFromDNSName(ctx, lbDNSName)
172+
if err != nil && h.retryer.IsErrorRetryable(err) {
173+
framework.Logf("transient error describing load balancers (will retry): %v", err)
267174
return false, nil
268175
}
176+
if err != nil {
177+
framework.Logf("permanent error describing load balancers: %v", err)
178+
return true, err
179+
}
269180
return true, nil
270181
})
271182

272183
if err != nil {
273-
return nil, fmt.Errorf("failed to find load balancer %s within timeout: %v", lbDNSName, err)
184+
return nil, fmt.Errorf("failed to find load balancer %s within timeout: %w", lbDNSName, err)
274185
}
275186

276187
return foundLB, nil
277188
}
189+
190+
// GetLoadBalancerFromDNSNameWithRetry describes a load balancers filtered by DNS name with
191+
// default retry values.
192+
// The default timeout is 20 minutes based on the AWS API limits and different regions
193+
// where DNS propagation.
194+
func (h *E2ETestHelperAWS) GetLoadBalancerFromDNSNameWithRetry(ctx context.Context, lbDNSName string) (*elbv2types.LoadBalancer, error) {
195+
return h.GetLoadBalancerFromDNSNameWithTimeout(ctx, lbDNSName, DefaultRetryTimeoutMinutes*time.Minute)
196+
}

0 commit comments

Comments
 (0)