Skip to content

Commit 4d66ea3

Browse files
Merge pull request #784 from dmage/iso-region-ipv6
Bug 2083466: Disable dualstack when it is not available
2 parents 1a500e0 + f2d322c commit 4d66ea3

File tree

2 files changed

+131
-2
lines changed

2 files changed

+131
-2
lines changed

pkg/storage/s3/s3.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,22 @@ func (er *endpointsResolver) EndpointFor(service, region string, opts ...func(*e
8383
return endpoints.DefaultResolver().EndpointFor(service, region, opts...)
8484
}
8585

86+
func isUnknownEndpointError(err error) bool {
87+
_, ok := err.(endpoints.UnknownEndpointError)
88+
return ok
89+
}
90+
91+
func regionHasDualStackS3(region string) (bool, error) {
92+
_, err := endpoints.DefaultResolver().EndpointFor("s3", region, endpoints.UseDualStackEndpointOption)
93+
if isUnknownEndpointError(err) {
94+
return false, nil
95+
}
96+
if err != nil {
97+
return false, err
98+
}
99+
return true, nil
100+
}
101+
86102
type driver struct {
87103
Context context.Context
88104
Config *imageregistryv1.ImageRegistryConfigStorageS3
@@ -237,6 +253,18 @@ func (d *driver) CABundle() (string, bool, error) {
237253
}
238254
}
239255

256+
// useDualStack returns true if the driver should use dual-stack endpoints
257+
func (d *driver) useDualStack() (bool, error) {
258+
if d.Config.RegionEndpoint != "" {
259+
return true, nil
260+
}
261+
ok, err := regionHasDualStackS3(d.Config.Region)
262+
if err != nil {
263+
return false, fmt.Errorf("failed to determine if region %s has dual stack S3: %w", d.Config.Region, err)
264+
}
265+
return ok, nil
266+
}
267+
240268
// getS3Service returns a client that allows us to interact
241269
// with the aws S3 service
242270
func (d *driver) getS3Service() (*s3.S3, error) {
@@ -309,7 +337,14 @@ func (d *driver) getS3Service() (*s3.S3, error) {
309337
awsOptions.Config.HTTPClient.Transport = d.roundTripper
310338
}
311339

312-
awsOptions.Config.WithUseDualStack(true)
340+
useDualStack, err := d.useDualStack()
341+
if err != nil {
342+
return nil, err
343+
}
344+
if useDualStack {
345+
awsOptions.Config.WithUseDualStack(true)
346+
}
347+
313348
if d.Config.RegionEndpoint != "" {
314349
if !d.Config.VirtualHostedStyle {
315350
awsOptions.Config.WithS3ForcePathStyle(true)
@@ -377,10 +412,17 @@ func (d *driver) ConfigEnv() (envs envvar.List, err error) {
377412
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_REGION", Value: d.Config.Region},
378413
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_ENCRYPT", Value: d.Config.Encrypt},
379414
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_VIRTUALHOSTEDSTYLE", Value: d.Config.VirtualHostedStyle},
380-
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_USEDUALSTACK", Value: true},
381415
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_CREDENTIALSCONFIGPATH", Value: filepath.Join(imageRegistrySecretMountpoint, imageRegistrySecretDataKey)},
382416
)
383417

418+
useDualStack, err := d.useDualStack()
419+
if err != nil {
420+
return nil, err
421+
}
422+
if useDualStack {
423+
envs = append(envs, envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_USEDUALSTACK", Value: true})
424+
}
425+
384426
if d.Config.CloudFront != nil {
385427
// Use structs to make ordering deterministic
386428
type cloudFrontOptions struct {

pkg/storage/s3/s3_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"testing"
1313

1414
"github.com/aws/aws-sdk-go/aws"
15+
"github.com/aws/aws-sdk-go/aws/endpoints"
1516
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
1617
"github.com/aws/aws-sdk-go/service/s3"
1718
"github.com/google/go-cmp/cmp"
@@ -27,6 +28,92 @@ import (
2728
"github.com/openshift/cluster-image-registry-operator/pkg/envvar"
2829
)
2930

31+
func TestEndpointsResolver(t *testing.T) {
32+
testCases := []struct {
33+
region string
34+
useDualStack bool
35+
endpoint string
36+
}{
37+
{
38+
region: "us-east-1",
39+
endpoint: "https://s3.amazonaws.com",
40+
},
41+
{
42+
region: "us-east-1",
43+
useDualStack: true,
44+
endpoint: "https://s3.dualstack.us-east-1.amazonaws.com",
45+
},
46+
{
47+
region: "us-gov-east-1",
48+
endpoint: "https://s3.us-gov-east-1.amazonaws.com",
49+
},
50+
{
51+
region: "us-gov-east-1",
52+
useDualStack: true,
53+
endpoint: "https://s3.dualstack.us-gov-east-1.amazonaws.com",
54+
},
55+
{
56+
region: "us-iso-east-1",
57+
endpoint: "https://s3.us-iso-east-1.c2s.ic.gov",
58+
},
59+
{
60+
region: "us-iso-east-1",
61+
useDualStack: true,
62+
endpoint: "",
63+
},
64+
}
65+
for _, tc := range testCases {
66+
t.Run(tc.region, func(t *testing.T) {
67+
er := newEndpointsResolver(tc.region, "", nil)
68+
var opts []func(*endpoints.Options)
69+
if tc.useDualStack {
70+
opts = append(opts, endpoints.UseDualStackEndpointOption)
71+
}
72+
ep, err := er.EndpointFor("s3", tc.region, opts...)
73+
if isUnknownEndpointError(err) && tc.endpoint == "" {
74+
return // the error is expected
75+
}
76+
if err != nil {
77+
t.Fatal(err)
78+
}
79+
if ep.URL != tc.endpoint {
80+
t.Errorf("got %s, want %s", ep.URL, tc.endpoint)
81+
}
82+
})
83+
}
84+
}
85+
86+
func TestRegionS3DualStack(t *testing.T) {
87+
testCases := []struct {
88+
region string
89+
want bool
90+
}{
91+
{
92+
region: "us-east-1",
93+
want: true,
94+
},
95+
{
96+
region: "us-gov-east-1",
97+
want: true,
98+
},
99+
{
100+
region: "us-iso-east-1",
101+
want: false,
102+
},
103+
}
104+
for _, tc := range testCases {
105+
t.Run(tc.region, func(t *testing.T) {
106+
got, err := regionHasDualStackS3(tc.region)
107+
if err != nil {
108+
t.Fatal(err)
109+
}
110+
if got != tc.want {
111+
t.Errorf("got %t, want %t", got, tc.want)
112+
}
113+
})
114+
}
115+
}
116+
30117
func TestGetConfig(t *testing.T) {
31118
testBuilder := cirofake.NewFixturesBuilder()
32119
testBuilder.AddInfraConfig(&configv1.Infrastructure{

0 commit comments

Comments
 (0)