Skip to content

Commit 55fc8db

Browse files
tthvok8s-infra-cherrypick-robot
authored andcommitted
🐛 fix: backwards compatibility for AWS service endpoint resolution
In AWS SDKv1, each service exports a constant EndpointsID to look up the custom service endpoint, for example, see ref [0]. In AWS SDKv2, these contants are no longer available. Thus, for backwards compatibility, we copy those constants from the SDKv1 and map them to the corresponding ServiceID in SDK v2. Additionally, in AWS SDKv1, elb and elbv2 uses the same identifier (i.e. same EndpointsID), thus the same custom endpoint [1][2]. For backwards compatibility, if elbv2 endpoint is undefined, elbv2 endpoint resolver should fall back to elb endpoint if any. This also fixes the bug where CAPA does not recognize services that define its serviceID with more than 1 word due to the incorrect assumption [3]. References: [0] https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/resourcegroupstaggingapi/service.go#L33-L34 [1] https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/elbv2/service.go#L32-L33 [2] https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/elb/service.go#L32-L33 [2] https://github.com/kubernetes-sigs/cluster-api-provider-aws/blob/88cb4b92b1a76591623e9d5ef347bfdc22010622/pkg/cloud/endpoints/endpoints.go#L90-L94
1 parent 077018e commit 55fc8db

File tree

2 files changed

+91
-17
lines changed

2 files changed

+91
-17
lines changed

pkg/cloud/endpoints/endpoints.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ var (
4646
errServiceEndpointServiceID = errors.New("must use a valid serviceID from the AWS GO SDK")
4747
errServiceEndpointDuplicateServiceID = errors.New("same serviceID defined twice for signing region")
4848
serviceEndpointsMap = map[string]serviceEndpoint{}
49+
compatServiceIDMap = map[string]string{
50+
"s3": s3.ServiceID,
51+
"elasticloadbalancing": elb.ServiceID,
52+
"ec2": ec2.ServiceID,
53+
"tagging": rgapi.ServiceID,
54+
"sqs": sqs.ServiceID,
55+
"events": eventbridge.ServiceID,
56+
"eks": eks.ServiceID,
57+
"ssm": ssm.ServiceID,
58+
"sts": sts.ServiceID,
59+
"secretsmanager": secretsmanager.ServiceID,
60+
}
4961
)
5062

5163
// serviceEndpoint contains AWS Service resolution information for SDK V2.
@@ -87,11 +99,13 @@ func ParseFlag(serviceEndpoints string) error {
8799
}
88100
seenServices = append(seenServices, serviceID)
89101

90-
// convert service ID to UpperCase as service IDs in AWS SDK GO V2 are UpperCase & Go map is Case Sensitve
91-
// This is for backward compabitibility
92-
// Ref: SDK V2 https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ec2#pkg-constants
93-
// Ref: SDK V1 https://pkg.go.dev/github.com/aws/aws-sdk-go/aws/endpoints#pkg-constants
94-
serviceID = strings.ToUpper(serviceID)
102+
// In v1 sdk, a constant EndpointsID is exported in each service to look up the custom service endpoint.
103+
// For example: https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/resourcegroupstaggingapi/service.go#L33-L34
104+
// In v2 SDK, these constants are no longer available.
105+
// For backwards compatibility, we copy those constants from the SDK v1 and map it to ServiceID in SDK v2.
106+
if v2serviceID, ok := compatServiceIDMap[serviceID]; ok {
107+
serviceID = v2serviceID
108+
}
95109

96110
URL, err := url.ParseRequestURI(kv[1])
97111
if err != nil {
@@ -104,6 +118,20 @@ func ParseFlag(serviceEndpoints string) error {
104118
}
105119
serviceEndpointsMap[serviceID] = endpoint
106120
}
121+
122+
// In v1 SDK, elb and elbv2 uses the same identifier, thus the same endpoint.
123+
// elbv2: https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/elbv2/service.go#L32-L33
124+
// elb: https://github.com/aws/aws-sdk-go/blob/070853e88d22854d2355c2543d0958a5f76ad407/service/elb/service.go#L32-L33
125+
// For backwards compatibility, if elbv2 endpoint is undefined, the elbv2 endpoint resolver should fall back to elb endpoint if any.
126+
if _, ok := serviceEndpointsMap[elbv2.ServiceID]; !ok {
127+
if elbEp, ok := serviceEndpointsMap[elb.ServiceID]; ok {
128+
serviceEndpointsMap[elbv2.ServiceID] = serviceEndpoint{
129+
ServiceID: elbv2.ServiceID,
130+
URL: elbEp.URL,
131+
SigningRegion: elbEp.SigningRegion,
132+
}
133+
}
134+
}
107135
}
108136
return nil
109137
}

pkg/cloud/endpoints/endpoints_test.go

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,60 @@ package endpoints
1919
import (
2020
"errors"
2121
"testing"
22+
23+
"github.com/google/go-cmp/cmp"
2224
)
2325

2426
func TestParseFlags(t *testing.T) {
2527
testCases := []struct {
26-
name string
27-
flagToParse string
28-
expectedError error
28+
name string
29+
flagToParse string
30+
expectedError error
31+
expectedServiceEndpointsMap map[string]serviceEndpoint
2932
}{
3033
{
31-
name: "no configuration",
32-
flagToParse: "",
33-
expectedError: nil,
34+
name: "no configuration",
35+
flagToParse: "",
36+
expectedServiceEndpointsMap: make(map[string]serviceEndpoint),
3437
},
3538
{
36-
name: "single region, single service",
37-
flagToParse: "us-iso:ec2=https://localhost:8080",
38-
expectedError: nil,
39+
name: "single region, single service",
40+
flagToParse: "us-iso:ec2=https://localhost:8080",
41+
expectedServiceEndpointsMap: map[string]serviceEndpoint{"EC2": {ServiceID: "EC2", URL: "https://localhost:8080", SigningRegion: "us-iso"}},
3942
},
4043
{
41-
name: "single region, multiple services",
42-
flagToParse: "us-iso:ec2=https://localhost:8080,sts=https://elbhost:8080",
43-
expectedError: nil,
44+
name: "single region, multiple services with v1 service IDs",
45+
flagToParse: "us-iso:s3=https://s3.com,elasticloadbalancing=https://elb.com,ec2=https://ec2.com,tagging=https://tagging.com,sqs=https://sqs.com,events=https://events.com,eks=https://eks.com,ssm=https://ssm.com,sts=https://sts.com,secretsmanager=https://secretmanager.com",
46+
expectedServiceEndpointsMap: map[string]serviceEndpoint{
47+
"EC2": {ServiceID: "EC2", URL: "https://ec2.com", SigningRegion: "us-iso"},
48+
"EKS": {ServiceID: "EKS", URL: "https://eks.com", SigningRegion: "us-iso"},
49+
"Elastic Load Balancing": {ServiceID: "Elastic Load Balancing", URL: "https://elb.com", SigningRegion: "us-iso"},
50+
"Elastic Load Balancing v2": {ServiceID: "Elastic Load Balancing v2", URL: "https://elb.com", SigningRegion: "us-iso"},
51+
"EventBridge": {ServiceID: "EventBridge", URL: "https://events.com", SigningRegion: "us-iso"},
52+
"Resource Groups Tagging API": {ServiceID: "Resource Groups Tagging API", URL: "https://tagging.com", SigningRegion: "us-iso"},
53+
"S3": {ServiceID: "S3", URL: "https://s3.com", SigningRegion: "us-iso"},
54+
"SQS": {ServiceID: "SQS", URL: "https://sqs.com", SigningRegion: "us-iso"},
55+
"SSM": {ServiceID: "SSM", URL: "https://ssm.com", SigningRegion: "us-iso"},
56+
"STS": {ServiceID: "STS", URL: "https://sts.com", SigningRegion: "us-iso"},
57+
"Secrets Manager": {ServiceID: "Secrets Manager", URL: "https://secretmanager.com", SigningRegion: "us-iso"},
58+
},
59+
},
60+
{
61+
name: "single region, multiple services with v2 service IDs",
62+
flagToParse: "us-iso:S3=https://s3.com,Elastic Load Balancing=https://elb.com,Elastic Load Balancing v2=https://elbv2.com,EC2=https://ec2.com,Resource Groups Tagging API=https://tagging.com,SQS=https://sqs.com,EventBridge=https://events.com,EKS=https://eks.com,SSM=https://ssm.com,STS=https://sts.com,Secrets Manager=https://secretmanager.com",
63+
expectedServiceEndpointsMap: map[string]serviceEndpoint{
64+
"EC2": {ServiceID: "EC2", URL: "https://ec2.com", SigningRegion: "us-iso"},
65+
"EKS": {ServiceID: "EKS", URL: "https://eks.com", SigningRegion: "us-iso"},
66+
"Elastic Load Balancing": {ServiceID: "Elastic Load Balancing", URL: "https://elb.com", SigningRegion: "us-iso"},
67+
"Elastic Load Balancing v2": {ServiceID: "Elastic Load Balancing v2", URL: "https://elbv2.com", SigningRegion: "us-iso"},
68+
"EventBridge": {ServiceID: "EventBridge", URL: "https://events.com", SigningRegion: "us-iso"},
69+
"Resource Groups Tagging API": {ServiceID: "Resource Groups Tagging API", URL: "https://tagging.com", SigningRegion: "us-iso"},
70+
"S3": {ServiceID: "S3", URL: "https://s3.com", SigningRegion: "us-iso"},
71+
"SQS": {ServiceID: "SQS", URL: "https://sqs.com", SigningRegion: "us-iso"},
72+
"SSM": {ServiceID: "SSM", URL: "https://ssm.com", SigningRegion: "us-iso"},
73+
"STS": {ServiceID: "STS", URL: "https://sts.com", SigningRegion: "us-iso"},
74+
"Secrets Manager": {ServiceID: "Secrets Manager", URL: "https://secretmanager.com", SigningRegion: "us-iso"},
75+
},
4476
},
4577
{
4678
name: "single region, duplicate service",
@@ -56,6 +88,10 @@ func TestParseFlags(t *testing.T) {
5688
name: "multiples regions",
5789
flagToParse: "us-iso:ec2=https://localhost:8080,sts=https://elbhost:8080;gb-iso:ec2=https://localhost:8080,sts=https://elbhost:8080",
5890
expectedError: nil,
91+
expectedServiceEndpointsMap: map[string]serviceEndpoint{
92+
"EC2": {ServiceID: "EC2", URL: "https://localhost:8080", SigningRegion: "gb-iso"},
93+
"STS": {ServiceID: "STS", URL: "https://elbhost:8080", SigningRegion: "gb-iso"},
94+
},
5995
},
6096
{
6197
name: "invalid config",
@@ -66,11 +102,21 @@ func TestParseFlags(t *testing.T) {
66102

67103
for _, tc := range testCases {
68104
t.Run(tc.name, func(t *testing.T) {
105+
defer t.Cleanup(func() {
106+
serviceEndpointsMap = make(map[string]serviceEndpoint)
107+
})
108+
69109
err := ParseFlag(tc.flagToParse)
70110

71111
if !errors.Is(err, tc.expectedError) {
72112
t.Fatalf("did not expect correct error: got %v, expected %v", err, tc.expectedError)
73113
}
114+
115+
if err == nil {
116+
if !cmp.Equal(serviceEndpointsMap, tc.expectedServiceEndpointsMap) {
117+
t.Fatalf("expected serviceEndpointsMap: %#v, but got: %#v", tc.expectedServiceEndpointsMap, serviceEndpointsMap)
118+
}
119+
}
74120
})
75121
}
76122
}

0 commit comments

Comments
 (0)