11/*
2- Copyright 2025 The Kubernetes Authors.
2+ Copyright 2026 The Kubernetes Authors.
33
44Licensed under the Apache License, Version 2.0 (the "License");
55you 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.
7473func (h * E2ETestHelperAWS ) GetEC2Client () * ec2.Client {
7574 return h .ec2Client
7675}
7776
77+ // GetELBV2Client returns the ELBV2 client.
7878func (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