Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions pkg/aws/aws_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package aws

import (
"context"
"net/http"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
"github.com/aws/aws-sdk-go-v2/aws/ratelimit"
Expand All @@ -16,8 +19,15 @@ import (

const (
userAgent = "elbv2.k8s.aws"
// defaultAWSSDKClientTimeout is the timeout for individual HTTP requests made by AWS SDK clients.
defaultAWSSDKClientTimeout = 10 * time.Second
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10s is reasonable for individual requests, but since the LBC talks to 9+ AWS services with varying response profiles, I'd prefer if we can have a slightly larger value (like 15s/20s). Also I'm wondering how it handles throttling situation, or because it's per request timeout, won't affect the controller behavior when API is throttling

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throttling will be errored out quickly and having a timeout shouldn't be impacting existing behaviors on throttling.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to fail fast and 15 sec is a safe compromise and looks fine to me.

)

// newDefaultHTTPClient returns an http.Client with the standard AWS SDK timeout.
func newDefaultHTTPClient() *http.Client {
return &http.Client{Timeout: defaultAWSSDKClientTimeout}
}

func NewAWSConfigGenerator(cfg CloudConfig, ec2IMDSEndpointMode imds.EndpointModeState, metricsCollector *awsmetrics.Collector) AWSConfigGenerator {
return &awsConfigGeneratorImpl{
cfg: cfg,
Expand All @@ -42,6 +52,7 @@ func (gen *awsConfigGeneratorImpl) GenerateAWSConfig(optFns ...func(*config.Load

defaultOpts := []func(*config.LoadOptions) error{
config.WithRegion(gen.cfg.Region),
config.WithHTTPClient(newDefaultHTTPClient()),
config.WithRetryer(func() aws.Retryer {
return retry.NewStandard(func(o *retry.StandardOptions) {
o.RateLimiter = ratelimit.None
Expand Down
25 changes: 25 additions & 0 deletions pkg/aws/aws_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package aws

import (
"net/http"
"testing"

"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func Test_GenerateAWSConfig_SetsHTTPClientTimeout(t *testing.T) {
gen := NewAWSConfigGenerator(CloudConfig{
Region: "us-west-2",
MaxRetries: 3,
}, imds.EndpointModeStateIPv4, nil)

cfg, err := gen.GenerateAWSConfig()
require.NoError(t, err)
require.NotNil(t, cfg.HTTPClient)

httpClient, ok := cfg.HTTPClient.(*http.Client)
require.True(t, ok, "HTTPClient should be *http.Client")
assert.Equal(t, defaultAWSSDKClientTimeout, httpClient.Timeout)
}
16 changes: 12 additions & 4 deletions pkg/aws/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ func NewCloud(cfg CloudConfig, clusterName string, metricsCollector *aws_metrics
ec2IMDSEndpointMode = imds.EndpointModeStateIPv4
}
endpointsResolver := epresolver.NewResolver(cfg.AWSEndpoints)
ec2MetadataCfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRetryMaxAttempts(cfg.MaxRetries),
config.WithEC2IMDSEndpointMode(ec2IMDSEndpointMode),
)
ec2MetadataCfg, err := buildEC2MetadataConfig(cfg.MaxRetries, ec2IMDSEndpointMode)
if err != nil {
return nil, errors.Wrap(err, "failed to build EC2 metadata config")
}
ec2Metadata := services.NewEC2Metadata(ec2MetadataCfg, endpointsResolver)

if len(cfg.Region) == 0 {
Expand Down Expand Up @@ -314,3 +314,11 @@ func (c *defaultCloud) Region() string {
func (c *defaultCloud) VpcID() string {
return c.cfg.VpcID
}

func buildEC2MetadataConfig(maxRetries int, ec2IMDSEndpointMode imds.EndpointModeState) (aws.Config, error) {
return config.LoadDefaultConfig(context.TODO(),
config.WithHTTPClient(newDefaultHTTPClient()),
config.WithRetryMaxAttempts(maxRetries),
config.WithEC2IMDSEndpointMode(ec2IMDSEndpointMode),
)
}
15 changes: 15 additions & 0 deletions pkg/aws/cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package aws
import (
"context"
"fmt"
"net/http"
"testing"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
ctrl "sigs.k8s.io/controller-runtime"
)
Expand Down Expand Up @@ -136,3 +139,15 @@ func Test_getVpcID(t *testing.T) {
})
}
}

func Test_buildEC2MetadataConfig_SetsHTTPClientTimeout(t *testing.T) {
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test_buildEC2MetadataConfig_SetsHTTPClientTimeout may be flaky/slow in CI because config.LoadDefaultConfig will try to resolve a region when none is configured (often via IMDS). To keep this unit test hermetic, set AWS_REGION/AWS_DEFAULT_REGION in the test (e.g., t.Setenv) before calling buildEC2MetadataConfig.

Suggested change
func Test_buildEC2MetadataConfig_SetsHTTPClientTimeout(t *testing.T) {
func Test_buildEC2MetadataConfig_SetsHTTPClientTimeout(t *testing.T) {
t.Setenv("AWS_REGION", "us-west-2")
t.Setenv("AWS_DEFAULT_REGION", "us-west-2")

Copilot uses AI. Check for mistakes.
t.Setenv("AWS_REGION", "us-west-2")
t.Setenv("AWS_DEFAULT_REGION", "us-west-2")
cfg, err := buildEC2MetadataConfig(3, imds.EndpointModeStateIPv4)
assert.NoError(t, err)
assert.NotNil(t, cfg.HTTPClient)

httpClient, ok := cfg.HTTPClient.(*http.Client)
require.True(t, ok, "HTTPClient should be *http.Client")
assert.Equal(t, defaultAWSSDKClientTimeout, httpClient.Timeout)
Comment on lines +150 to +152
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test uses assert.True on the type assertion, but then dereferences httpClient.Timeout unconditionally. If the assertion fails, httpClient will be nil and the test will panic; use require.True/require.NotNil (or guard the dereference) so failures are reported cleanly.

Copilot uses AI. Check for mistakes.
}