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
40 changes: 40 additions & 0 deletions pkg/asset/installconfig/aws/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/ec2"
elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/route53"
"github.com/aws/aws-sdk-go-v2/service/servicequotas"
Expand All @@ -32,6 +34,44 @@ func NewEC2Client(ctx context.Context, endpointOpts EndpointOptions, optFns ...f
return ec2.NewFromConfig(cfg, ec2Opts...), nil
}

// NewELBClient creates a new ELB (classic) client.
func NewELBClient(ctx context.Context, endpointOpts EndpointOptions, optFns ...func(*elb.Options)) (*elb.Client, error) {
cfg, err := GetConfigWithOptions(ctx, config.WithRegion(endpointOpts.Region))
if err != nil {
return nil, err
}

elbOpts := []func(*elb.Options){
func(o *elb.Options) {
o.EndpointResolverV2 = &ELBEndpointResolver{
ServiceEndpointResolver: NewServiceEndpointResolver(endpointOpts),
}
},
}
elbOpts = append(elbOpts, optFns...)

return elb.NewFromConfig(cfg, elbOpts...), nil
}

// NewELBV2Client creates a new ELBV2 client.
func NewELBV2Client(ctx context.Context, endpointOpts EndpointOptions, optFns ...func(*elbv2.Options)) (*elbv2.Client, error) {
cfg, err := GetConfigWithOptions(ctx, config.WithRegion(endpointOpts.Region))
if err != nil {
return nil, err
}

elbv2Opts := []func(*elbv2.Options){
func(o *elbv2.Options) {
o.EndpointResolverV2 = &ELBV2EndpointResolver{
ServiceEndpointResolver: NewServiceEndpointResolver(endpointOpts),
}
},
}
elbv2Opts = append(elbv2Opts, optFns...)

return elbv2.NewFromConfig(cfg, elbv2Opts...), nil
}

// NewIAMClient creates a new IAM API client.
func NewIAMClient(ctx context.Context, endpointOpts EndpointOptions, optFns ...func(*iam.Options)) (*iam.Client, error) {
cfg, err := GetConfigWithOptions(ctx, config.WithRegion(endpointOpts.Region))
Expand Down
86 changes: 72 additions & 14 deletions pkg/asset/installconfig/aws/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,26 @@ const (
)

var (
// v1Tov2ServiceIDMap maps v1 service ID to its v2 equivalent.
v1Tov2ServiceIDMap = map[string]string{
"ec2": ec2.ServiceID,
"elasticloadbalancing": elb.ServiceID,
"elasticloadbalancingv2": elbv2.ServiceID,
"iam": iam.ServiceID,
"route53": route53.ServiceID,
"s3": s3.ServiceID,
"sts": sts.ServiceID,
"resourcegroupstaggingapi": resourcegroupstaggingapi.ServiceID,
"servicequotas": servicequotas.ServiceID,
// In v1 sdk, a constant EndpointsID is exported in each service to look up the custom service endpoint.
// For example: https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/resourcegroupstaggingapi/service.go#L33-L34
// In v2 SDK, these constants are no longer available.
// For backwards compatibility, we copy those constants from the SDK v1 and map it to ServiceID in SDK v2.
compatServiceIDMap = map[string]string{
"ec2": ec2.ServiceID,
"elasticloadbalancing": elb.ServiceID,
"iam": iam.ServiceID,
"route53": route53.ServiceID,
"s3": s3.ServiceID,
"sts": sts.ServiceID,
"tagging": resourcegroupstaggingapi.ServiceID,
"servicequotas": servicequotas.ServiceID,
}
)

// resolveServiceID converts a service ID in the SDK from v1 to v2.
// If the service ID is not recognized, return as-is.
// resolveServiceID returns the serviceID for service endpoint resolvers to look up the endpoint URL.
// If the serviceID is an SDKv1 identifier, this converts it SDKv2. Otherwise, return as-is.
func resolveServiceID(serviceID string) string {
if v2serviceID, ok := v1Tov2ServiceIDMap[serviceID]; ok {
if v2serviceID, ok := compatServiceIDMap[serviceID]; ok {
return v2serviceID
}
return serviceID
Expand Down Expand Up @@ -76,6 +78,20 @@ func NewServiceEndpointResolver(opts EndpointOptions) *ServiceEndpointResolver {
for _, endpoint := range opts.Endpoints {
endpointMap[resolveServiceID(endpoint.Name)] = endpoint
}

// In v1 SDK, elb and elbv2 uses the same identifier, thus the same endpoint.
// elbv2: https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/elbv2/service.go#L32-L33
// elb: https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/elb/service.go#L32-L33
// For backwards compatibility, if elbv2 endpoint is undefined, the elbv2 endpoint resolver should fall back to elb endpoint if any.
if _, ok := endpointMap[elbv2.ServiceID]; !ok {
if elbEp, ok := endpointMap[elb.ServiceID]; ok {
endpointMap[elbv2.ServiceID] = typesaws.ServiceEndpoint{
Name: elbv2.ServiceID,
URL: elbEp.URL,
}
}
}

return &ServiceEndpointResolver{
endpoints: endpointMap,
endpointOptions: opts,
Expand Down Expand Up @@ -103,6 +119,48 @@ func (s *EC2EndpointResolver) ResolveEndpoint(ctx context.Context, params ec2.En
return ec2.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
}

// ELBEndpointResolver implements EndpointResolverV2 interface for ELB (classic).
type ELBEndpointResolver struct {
*ServiceEndpointResolver
}

// ResolveEndpoint for ELB.
func (s *ELBEndpointResolver) ResolveEndpoint(ctx context.Context, params elb.EndpointParameters) (smithyendpoints.Endpoint, error) {
params.UseDualStack = aws.Bool(s.endpointOptions.UseDualStack)
params.UseFIPS = aws.Bool(s.endpointOptions.UseFIPS)

// If custom endpoint not found, return default endpoint for the service.
endpoint, ok := s.endpoints[elb.ServiceID]
if !ok {
return elb.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
}

params.Endpoint = aws.String(endpoint.URL)
params.Region = aws.String(s.endpointOptions.Region)
return elb.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
}

// ELBV2EndpointResolver implements EndpointResolverV2 interface for ELBV2.
type ELBV2EndpointResolver struct {
*ServiceEndpointResolver
}

// ResolveEndpoint for ELBV2.
func (s *ELBV2EndpointResolver) ResolveEndpoint(ctx context.Context, params elbv2.EndpointParameters) (smithyendpoints.Endpoint, error) {
params.UseDualStack = aws.Bool(s.endpointOptions.UseDualStack)
params.UseFIPS = aws.Bool(s.endpointOptions.UseFIPS)

// If custom endpoint not found, return default endpoint for the service.
endpoint, ok := s.endpoints[elbv2.ServiceID]
if !ok {
return elbv2.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
}

params.Endpoint = aws.String(endpoint.URL)
params.Region = aws.String(s.endpointOptions.Region)
return elbv2.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
}

// IAMEndpointResolver implements EndpointResolverV2 interface for IAM.
type IAMEndpointResolver struct {
*ServiceEndpointResolver
Expand Down
16 changes: 5 additions & 11 deletions pkg/infrastructure/aws/clusterapi/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,20 +275,14 @@ func getHostedZoneIDForNLB(ctx context.Context, ic *installconfig.InstallConfig,
return hzID, nil
}

cfg, err := configv2.LoadDefaultConfig(ctx, configv2.WithRegion(ic.Config.Platform.AWS.Region))
client, err := awsconfig.NewELBV2Client(ctx, awsconfig.EndpointOptions{
Region: ic.Config.AWS.Region,
Endpoints: ic.Config.AWS.ServiceEndpoints,
})
if err != nil {
return "", fmt.Errorf("failed to load AWS config: %w", err)
return "", fmt.Errorf("failed to create elbv2 client: %w", err)
}

client := elbv2.NewFromConfig(cfg, func(options *elbv2.Options) {
options.Region = ic.Config.Platform.AWS.Region
for _, endpoint := range ic.Config.AWS.ServiceEndpoints {
if strings.EqualFold(endpoint.Name, "elasticloadbalancing") {
options.BaseEndpoint = aws.String(endpoint.URL)
}
}
})

// If the HostedZoneID is not known, query from the LoadBalancer
input := elbv2.DescribeLoadBalancersInput{
Names: []string{lbName},
Expand Down