Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fallback when AWS SDK returns invalid FIPS endpoint when global UseFIPSEndpoint is set #38057

Merged
merged 17 commits into from
Jun 20, 2024
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .changelog/38057.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
provider: Now falls back to non-FIPS endpoint if `use_fips_endpoint` is set and no FIPS endpoint is available
```
46 changes: 35 additions & 11 deletions docs/add-a-new-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,22 @@ If an AWS service must be created in a non-standard way, for example, the servic
func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*costoptimizationhub.Client, error) {
cfg := *(config["aws_sdkv2_config"].(*aws.Config))

return costoptimizationhub.NewFromConfig(cfg, func(o *costoptimizationhub.Options) {
if endpoint := config["endpoint"].(string); endpoint != "" {
o.BaseEndpoint = aws.String(endpoint)
} else if config["partition"].(string) == names.StandardPartitionID {
// Cost Optimization Hub endpoint is available only in us-east-1 Region.
o.Region = names.USEast1RegionID
}
}), nil
return costoptimizationhub.NewFromConfig(cfg,
costoptimizationhub.WithEndpointResolverV2(newEndpointResolverSDKv2()),
withBaseEndpoint(config[names.AttrEndpoint].(string)),
func(o *costoptimizationhub.Options) {
if config["partition"].(string) == names.StandardPartitionID {
// Cost Optimization Hub endpoint is available only in us-east-1 Region.
if cfg.Region != names.USEast1RegionID {
tflog.Info(ctx, "overriding region", map[string]any{
"original_region": cfg.Region,
"override_region": names.USEast1RegionID,
})
o.Region = names.USEast1RegionID
}
}
},
), nil
}
```

Expand All @@ -121,11 +129,27 @@ If an AWS service must be created in a non-standard way, for example, the servic
// NewConn returns a new AWS SDK for Go v1 client for this service package's AWS API.
func (p *servicePackage) NewConn(ctx context.Context) (*globalaccelerator_sdkv1.GlobalAccelerator, error) {
sess := p.config["session"].(*session_sdkv1.Session)
config := &aws_sdkv1.Config{Endpoint: aws_sdkv1.String(p.config["endpoint"].(string))}

cfg := aws.Config{}

if endpoint := config[names.AttrEndpoint].(string); endpoint != "" {
tflog.Debug(ctx, "setting endpoint", map[string]any{
"tf_aws.endpoint": endpoint,
})
cfg.Endpoint = aws.String(endpoint)
} else {
cfg.EndpointResolver = newEndpointResolverSDKv1(ctx)
}

// Force "global" services to correct Regions.
if p.config["partition"].(string) == endpoints_sdkv1.AwsPartitionID {
config.Region = aws_sdkv1.String(endpoints_sdkv1.UsWest2RegionID)
if config["partition"].(string) == endpoints.AwsPartitionID {
if aws.StringValue(cfg.Region) != endpoints.UsWest2RegionID {
tflog.Info(ctx, "overriding region", map[string]any{
"original_region": aws.StringValue(cfg.Region),
"override_region": endpoints.UsWest2RegionID,
})
cfg.Region = aws.String(endpoints.UsWest2RegionID)
}
}

return globalaccelerator_sdkv1.New(sess.Copy(config)), nil
Expand Down
24 changes: 14 additions & 10 deletions docs/retries-and-waiters.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,20 @@ When custom service client configurations are applied, these will be defined in
func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*s3_sdkv2.Client, error) {
cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config))

return s3_sdkv2.NewFromConfig(cfg, func(o *s3_sdkv2.Options) {
// ..other configuration..

o.Retryer = conns.AddIsErrorRetryables(cfg.Retryer().(aws_sdkv2.RetryerV2), retry_sdkv2.IsErrorRetryableFunc(func(err error) aws_sdkv2.Ternary {
if tfawserr_sdkv2.ErrMessageContains(err, errCodeOperationAborted, "A conflicting conditional operation is currently in progress against this resource. Please try again.") {
return aws_sdkv2.TrueTernary
}
return aws_sdkv2.UnknownTernary // Delegate to configured Retryer.
}))
}), nil
return s3_sdkv2.NewFromConfig(cfg,
s3.WithEndpointResolverV2(newEndpointResolverSDKv2()),
withBaseEndpoint(config[names.AttrEndpoint].(string)),
func(o *s3_sdkv2.Options) {
// ..other configuration..

o.Retryer = conns.AddIsErrorRetryables(cfg.Retryer().(aws_sdkv2.RetryerV2), retry_sdkv2.IsErrorRetryableFunc(func(err error) aws_sdkv2.Ternary {
if tfawserr_sdkv2.ErrMessageContains(err, errCodeOperationAborted, "A conflicting conditional operation is currently in progress against this resource. Please try again.") {
return aws_sdkv2.TrueTernary
}
return aws_sdkv2.UnknownTernary // Delegate to configured Retryer.
}))
},
), nil
}
```

Expand Down
59 changes: 40 additions & 19 deletions internal/generate/serviceendpointtests/file.gtpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import (
{{- end }}
"fmt"
"maps"
{{- if and (ne .GoV1Package "") (eq .GoV2Package "") }}
"net"
"net/url"
{{- end }}
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -133,7 +132,7 @@ func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.S
testcases := map[string]endpointTestCase{
"no config": {
with: []setupFunc{withNoConfig},
expected: expectDefaultEndpoint(expectedEndpointRegion),
expected: expectDefaultEndpoint(t, expectedEndpointRegion),
},

// Package name endpoint on Config
Expand Down Expand Up @@ -474,7 +473,7 @@ func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.S
with: []setupFunc{
withUseFIPSInConfig,
},
expected: expectDefaultFIPSEndpoint(expectedEndpointRegion),
expected: expectDefaultFIPSEndpoint(t, expectedEndpointRegion),
},

"use fips config with package name endpoint config": {
Expand Down Expand Up @@ -517,22 +516,22 @@ func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.S
{{ end -}}
}

func defaultEndpoint(region string) string {
func defaultEndpoint(region string) (url.URL, error) {
{{- if ne .GoV2Package "" }}
r := {{ .GoV2Package }}_sdkv2.NewDefaultEndpointResolverV2()

ep, err := r.ResolveEndpoint(context.Background(), {{ .GoV2Package }}_sdkv2.EndpointParameters{
Region: aws_sdkv2.String(region),
})
if err != nil {
return err.Error()
return url.URL{}, err
}

if ep.URI.Path == "" {
ep.URI.Path = "/"
}

return ep.URI.String()
return ep.URI, nil
{{ else }}
r := endpoints.DefaultResolver()

Expand All @@ -543,7 +542,7 @@ func defaultEndpoint(region string) string {
{{- end -}}
)
if err != nil {
return err.Error()
return url.URL{}, err
}

url, _ := url.Parse(ep.URL)
Expand All @@ -552,11 +551,11 @@ func defaultEndpoint(region string) string {
url.Path = "/"
}

return url.String()
return *url, nil
{{ end -}}
}

func defaultFIPSEndpoint(region string) string {
func defaultFIPSEndpoint(region string) (url.URL, error) {
{{- if ne .GoV2Package "" }}
r := {{ .GoV2Package }}_sdkv2.NewDefaultEndpointResolverV2()

Expand All @@ -565,14 +564,14 @@ func defaultFIPSEndpoint(region string) string {
UseFIPS: aws_sdkv2.Bool(true),
})
if err != nil {
return err.Error()
return url.URL{}, err
}

if ep.URI.Path == "" {
ep.URI.Path = "/"
}

return ep.URI.String()
return ep.URI, nil
{{ else }}
r := endpoints.DefaultResolver()

Expand All @@ -581,7 +580,7 @@ func defaultFIPSEndpoint(region string) string {
opt.UseFIPSEndpoint = endpoints.FIPSEndpointStateEnabled
})
if err != nil {
return err.Error()
return url.URL{}, err
}

url, _ := url.Parse(ep.URL)
Expand All @@ -590,7 +589,7 @@ func defaultFIPSEndpoint(region string) string {
url.Path = "/"
}

return url.String()
return *url, nil
{{ end -}}
}

Expand Down Expand Up @@ -716,16 +715,38 @@ func withUseFIPSInConfig(setup *caseSetup) {
setup.config["use_fips_endpoint"] = true
}

func expectDefaultEndpoint(region string) caseExpectations {
func expectDefaultEndpoint(t *testing.T, region string) caseExpectations {
t.Helper()

endpoint, err := defaultEndpoint(region)
if err != nil {
t.Fatalf("resolving accessanalyzer default endpoint: %s", err)
}

return caseExpectations{
endpoint: defaultEndpoint(region),
endpoint: endpoint.String(),
region: expectedCallRegion,
}
}

func expectDefaultFIPSEndpoint(region string) caseExpectations {
return caseExpectations{
endpoint: defaultFIPSEndpoint(region),
func expectDefaultFIPSEndpoint(t *testing.T, region string) caseExpectations {
t.Helper()

endpoint, err := defaultFIPSEndpoint(region)
if err != nil {
t.Fatalf("resolving accessanalyzer FIPS endpoint: %s", err)
}

hostname := endpoint.Hostname()
_, err = net.LookupHost(hostname)
if dnsErr, ok := errs.As[*net.DNSError](err); ok && dnsErr.IsNotFound {
return expectDefaultEndpoint(t, region)
} else if err != nil {
t.Fatalf("looking up accessanalyzer endpoint %q: %s", hostname, err)
}

return caseExpectations{
endpoint: endpoint.String(),
region: expectedCallRegion,
}
}
Expand Down
Loading
Loading