Skip to content
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
  •  
  •  
  •  
9 changes: 7 additions & 2 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
codecov:
token: 47dfd1fc-dc72-4d25-b608-eca6508efd05

# Removed hardcoded token for security reasons.
# The token should be set via CI/CD secrets instead.
# Example for GitHub Actions:
# - name: Upload coverage to Codecov
# run: codecov -t ${{ secrets.CODECOV_TOKEN }}
#
# See: https://docs.codecov.com/docs/adding-the-codecov-token
coverage:
status:
project:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ jobs:
os: [ubuntu-latest, macos-latest]
# When updating this, make sure to also update the
# latest_go_version variable in internal/testing/runchecks.sh.
go-version: [1.24.x]
go-version: [1.25.x]
include:
- go-version: 1.23.x
- go-version: 1.24.x
os: ubuntu-latest

runs-on: ${{ matrix.os }}
Expand Down
214 changes: 56 additions & 158 deletions aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,165 +20,47 @@ import (
"fmt"
"net/url"
"strconv"
"strings"

awsv2 "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/ratelimit"
"github.com/aws/aws-sdk-go-v2/aws/retry"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/google/wire"
)

// DefaultSession is a Wire provider set that provides a *session.Session using
// the default options.
var DefaultSession = wire.NewSet(
SessionConfig,
ConfigCredentials,
NewDefaultSession,
wire.Bind(new(client.ConfigProvider), new(*session.Session)),
const (
requestChecksumCalculationParamKey = "request_checksum_calculation"
responseChecksumValidationParamKey = "response_checksum_validation"
)

// NewDefaultSession returns a *session.Session using the default options.
func NewDefaultSession() (*session.Session, error) {
return session.NewSessionWithOptions(session.Options{SharedConfigState: session.SharedConfigEnable})
}

// SessionConfig returns sess.Config.
func SessionConfig(sess *session.Session) *aws.Config {
return sess.Config
}

// ConfigCredentials returns cfg.Credentials.
func ConfigCredentials(cfg *aws.Config) *credentials.Credentials {
return cfg.Credentials
}

// ConfigOverrider implements client.ConfigProvider by overlaying a list of
// configurations over a base configuration provider.
type ConfigOverrider struct {
Base client.ConfigProvider
Configs []*aws.Config
}

// ClientConfig calls the base provider's ClientConfig method with co.Configs
// followed by the arguments given to ClientConfig.
func (co ConfigOverrider) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config {
cfgs = append(co.Configs[:len(co.Configs):len(co.Configs)], cfgs...)
return co.Base.ClientConfig(serviceName, cfgs...)
}

// ConfigFromURLParams returns an aws.Config initialized based on the URL
// parameters in q. It is intended to be used by URLOpeners for AWS services.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
//
// It returns an error if q contains any unknown query parameters; callers
// should remove any query parameters they know about from q before calling
// ConfigFromURLParams.
//
// The following query options are supported:
// - region: The AWS region for requests; sets aws.Config.Region.
// - endpoint: The endpoint URL (hostname only or fully qualified URI); sets aws.Config.Endpoint.
// - disable_ssl (or disableSSL): A value of "true" disables SSL when sending requests; sets aws.Config.DisableSSL.
// - s3_force_path_style (or s3ForcePathStyle): A value of "true" forces the request to use path-style addressing; sets aws.Config.S3ForcePathStyle.
// - dualstack: A value of "true" enables dual stack (IPv4 and IPv6) endpoints
// - fips: A value of "true" enables the use of FIPS endpoints
func ConfigFromURLParams(q url.Values) (*aws.Config, error) {
var cfg aws.Config
for param, values := range q {
value := values[0]
switch param {
case "region":
cfg.Region = aws.String(value)
case "endpoint":
cfg.Endpoint = aws.String(value)
case "disable_ssl", "disableSSL":
b, err := strconv.ParseBool(value)
if err != nil {
return nil, fmt.Errorf("invalid value for query parameter %q: %v", param, err)
}
cfg.DisableSSL = aws.Bool(b)
case "s3_force_path_style", "s3ForcePathStyle":
b, err := strconv.ParseBool(value)
if err != nil {
return nil, fmt.Errorf("invalid value for query parameter %q: %v", param, err)
}
cfg.S3ForcePathStyle = aws.Bool(b)
case "dualstack":
b, err := strconv.ParseBool(value)
if err != nil {
return nil, fmt.Errorf("invalid value for query parameter %q: %v", param, err)
}
cfg.UseDualStack = aws.Bool(b)
case "fips":
b, err := strconv.ParseBool(value)
if err != nil {
return nil, fmt.Errorf("invalid value for query parameter %q: %v", param, err)
}
if b {
cfg.UseFIPSEndpoint = endpoints.FIPSEndpointStateEnabled
}
case "awssdk":
// ignore, should be handled before this
default:
return nil, fmt.Errorf("unknown query parameter %q", param)
}
// parseRequestChecksumCalculation parses request checksum calculation mode values.
// Supports AWS SDK documented values: "when_supported", "when_required".
func parseRequestChecksumCalculation(value string) (aws.RequestChecksumCalculation, error) {
switch strings.ToLower(value) {
case "when_supported":
return aws.RequestChecksumCalculationWhenSupported, nil
case "when_required":
return aws.RequestChecksumCalculationWhenRequired, nil
default:
return aws.RequestChecksumCalculationWhenSupported, fmt.Errorf("invalid value for %q: %q. Valid values are: when_supported, when_required", requestChecksumCalculationParamKey, value)
}
return &cfg, nil
}

// NewSessionFromURLParams returns an session.Session with session.Options initialized based on the URL
// parameters in q. It is intended to be used by URLOpeners for AWS services.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/session/#Session
//
// It should be used before ConfigFromURLParams as it strips the query
// parameters it knows about
//
// The following query options are supported:
// - profile: The AWS profile to use from the AWS configs (shared config file and
// shared credentials file)
func NewSessionFromURLParams(q url.Values) (*session.Session, url.Values, error) {
// always enable shared config (~/.aws/config by default)
opts := session.Options{SharedConfigState: session.SharedConfigEnable}
rest := url.Values{}
for param, values := range q {
value := values[0]
switch param {
case "profile":
opts.Profile = value
case "awssdk":
// ignore, should be handled before this
default:
rest.Add(param, value)
}
}
sess, err := session.NewSessionWithOptions(opts)
if err != nil {
return nil, nil, fmt.Errorf("couldn't create session %w", err)
}
return sess, rest, nil
}

// UseV2 returns true iff the URL parameters indicate that the provider
// should use the AWS SDK v2.
//
// "awssdk=v1" will force V1.
// "awssdk=v2" will force V2.
// No "awssdk" parameter (or any other value) will return the default, currently V2.
func UseV2(q url.Values) bool {
if values, ok := q["awssdk"]; ok {
if values[0] == "v1" || values[0] == "V1" {
return false
}
// parseResponseChecksumValidation parses response checksum validation mode values.
// Supports AWS SDK documented values: "when_supported", "when_required".
func parseResponseChecksumValidation(value string) (aws.ResponseChecksumValidation, error) {
switch strings.ToLower(value) {
case "when_supported":
return aws.ResponseChecksumValidationWhenSupported, nil
case "when_required":
return aws.ResponseChecksumValidationWhenRequired, nil
default:
return aws.ResponseChecksumValidationWhenSupported, fmt.Errorf("invalid value for %q: %q. Valid values are: when_supported, when_required", responseChecksumValidationParamKey, value)
}
return true
}

// NewDefaultV2Config returns a aws.Config for AWS SDK v2, using the default options.
func NewDefaultV2Config(ctx context.Context) (awsv2.Config, error) {
func NewDefaultV2Config(ctx context.Context) (aws.Config, error) {
return config.LoadDefaultConfig(ctx)
}

Expand All @@ -203,7 +85,9 @@ func NewDefaultV2Config(ctx context.Context) (awsv2.Config, error) {
// - rate_limiter_capacity: A integer value configures the capacity of a token bucket used
// in client-side rate limits. If no value is set, the client-side rate limiting is disabled.
// See https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/retries-timeouts/#client-side-rate-limiting.
func V2ConfigFromURLParams(ctx context.Context, q url.Values) (awsv2.Config, error) {
// - request_checksum_calculation: Request checksum calculation mode (when_supported, when_required)
// - response_checksum_validation: Response checksum validation mode (when_supported, when_required)
func V2ConfigFromURLParams(ctx context.Context, q url.Values) (aws.Config, error) {
var endpoint string
var hostnameImmutable bool
var rateLimitCapacity int64
Expand All @@ -215,7 +99,7 @@ func V2ConfigFromURLParams(ctx context.Context, q url.Values) (awsv2.Config, err
var err error
hostnameImmutable, err = strconv.ParseBool(value)
if err != nil {
return awsv2.Config{}, fmt.Errorf("invalid value for hostname_immutable: %w", err)
return aws.Config{}, fmt.Errorf("invalid value for hostname_immutable: %w", err)
}
case "region":
opts = append(opts, config.WithRegion(value))
Expand All @@ -226,43 +110,57 @@ func V2ConfigFromURLParams(ctx context.Context, q url.Values) (awsv2.Config, err
case "dualstack":
dualStack, err := strconv.ParseBool(value)
if err != nil {
return awsv2.Config{}, fmt.Errorf("invalid value for dualstack: %w", err)
return aws.Config{}, fmt.Errorf("invalid value for dualstack: %w", err)
}
if dualStack {
opts = append(opts, config.WithUseDualStackEndpoint(awsv2.DualStackEndpointStateEnabled))
opts = append(opts, config.WithUseDualStackEndpoint(aws.DualStackEndpointStateEnabled))
}
case "fips":
fips, err := strconv.ParseBool(value)
if err != nil {
return awsv2.Config{}, fmt.Errorf("invalid value for fips: %w", err)
return aws.Config{}, fmt.Errorf("invalid value for fips: %w", err)
}
if fips {
opts = append(opts, config.WithUseFIPSEndpoint(awsv2.FIPSEndpointStateEnabled))
opts = append(opts, config.WithUseFIPSEndpoint(aws.FIPSEndpointStateEnabled))
}
case "rate_limiter_capacity":
var err error
rateLimitCapacity, err = strconv.ParseInt(value, 10, 32)
if err != nil {
return awsv2.Config{}, fmt.Errorf("invalid value for capacity: %w", err)
return aws.Config{}, fmt.Errorf("invalid value for capacity: %w", err)
}
case "anonymous":
anon, err := strconv.ParseBool(value)
if err != nil {
return awsv2.Config{}, fmt.Errorf("invalid value for anonymous: %w", err)
return aws.Config{}, fmt.Errorf("invalid value for anonymous: %w", err)
}
if anon {
opts = append(opts, config.WithCredentialsProvider(awsv2.AnonymousCredentials{}))
opts = append(opts, config.WithCredentialsProvider(aws.AnonymousCredentials{}))
}
case requestChecksumCalculationParamKey:
value, err := parseRequestChecksumCalculation(value)
if err != nil {
return aws.Config{}, err
}

opts = append(opts, config.WithRequestChecksumCalculation(value))
case responseChecksumValidationParamKey:
value, err := parseResponseChecksumValidation(value)
if err != nil {
return aws.Config{}, err
}

opts = append(opts, config.WithResponseChecksumValidation(value))
case "awssdk":
// ignore, should be handled before this
default:
return awsv2.Config{}, fmt.Errorf("unknown query parameter %q", param)
return aws.Config{}, fmt.Errorf("unknown query parameter %q", param)
}
}
if endpoint != "" {
customResolver := awsv2.EndpointResolverWithOptionsFunc(
func(service, region string, options ...any) (awsv2.Endpoint, error) {
return awsv2.Endpoint{
customResolver := aws.EndpointResolverWithOptionsFunc(
func(service, region string, options ...any) (aws.Endpoint, error) {
return aws.Endpoint{
PartitionID: "aws",
URL: endpoint,
SigningRegion: region,
Expand All @@ -277,7 +175,7 @@ func V2ConfigFromURLParams(ctx context.Context, q url.Values) (awsv2.Config, err
if rateLimitCapacity > 0 {
rateLimiter = ratelimit.NewTokenRateLimit(uint(rateLimitCapacity))
}
opts = append(opts, config.WithRetryer(func() awsv2.Retryer {
opts = append(opts, config.WithRetryer(func() aws.Retryer {
return retry.NewStandard(func(so *retry.StandardOptions) {
so.RateLimiter = rateLimiter
})
Expand Down
Loading