Skip to content

Commit

Permalink
Add support for default configuration modes (#2624)
Browse files Browse the repository at this point in the history
  • Loading branch information
alextwoods committed Dec 21, 2021
1 parent 9e917e3 commit 378dfa6
Show file tree
Hide file tree
Showing 31 changed files with 873 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def default_plugins
'Aws::Plugins::ClientMetricsPlugin' => "#{core_plugins}/client_metrics_plugin.rb",
'Aws::Plugins::ClientMetricsSendPlugin' => "#{core_plugins}/client_metrics_send_plugin.rb",
'Aws::Plugins::TransferEncoding' => "#{core_plugins}/transfer_encoding.rb",
'Aws::Plugins::HttpChecksum' => "#{core_plugins}/http_checksum.rb"
'Aws::Plugins::HttpChecksum' => "#{core_plugins}/http_checksum.rb",
'Aws::Plugins::DefaultsMode' => "#{core_plugins}/defaults_mode.rb"
}
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module {{module_name}}
# seconds to wait when opening a HTTP session before raising a
# `Timeout::Error`.
#
# @option options [Integer] :http_read_timeout (60) The default
# @option options [Float] :http_read_timeout (60) The default
# number of seconds to wait for response data. This value can
# safely be set per-request on the session.
#
Expand All @@ -61,6 +61,9 @@ module {{module_name}}
# disables this behaviour. This value can safely be set per
# request on the session.
#
# @option options [Float] :ssl_timeout (nil) Sets the SSL timeout
# in seconds.
#
# @option options [Boolean] :http_wire_trace (false) When `true`,
# HTTP debug output will be sent to the `:logger`.
#
Expand Down
3 changes: 2 additions & 1 deletion build_tools/custom_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def build
'Aws::Plugins::RegionalEndpoint',
'Aws::Plugins::EndpointDiscovery',
'Aws::Plugins::EndpointPattern',
'Aws::Plugins::CredentialsConfiguration'
'Aws::Plugins::CredentialsConfiguration',
'Aws::Plugins::DefaultsMode'
]
)
end
Expand Down
2 changes: 1 addition & 1 deletion build_tools/services.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ServiceEnumerator
MANIFEST_PATH = File.expand_path('../../services.json', __FILE__)

# Minimum `aws-sdk-core` version for new gem builds
MINIMUM_CORE_VERSION = "3.122.0"
MINIMUM_CORE_VERSION = "3.125.0"

EVENTSTREAM_PLUGIN = "Aws::Plugins::EventStreamConfiguration"

Expand Down
2 changes: 2 additions & 0 deletions gems/aws-sdk-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Feature - Add `:defaults_mode` configuration - that determines how certain default configuration options are resolved in the SDK.

3.124.0 (2021-11-30)
------------------

Expand Down
3 changes: 3 additions & 0 deletions gems/aws-sdk-core/lib/aws-defaults.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require_relative 'aws-defaults/default_configuration'
153 changes: 153 additions & 0 deletions gems/aws-sdk-core/lib/aws-defaults/default_configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# frozen_string_literal: true

require_relative 'defaults_mode_config_resolver'

module Aws

# A defaults mode determines how certain default configuration options are resolved in the SDK.
#
# *Note*: For any mode other than `'legacy'` the vended default values might change as best practices may
# evolve. As a result, it is encouraged to perform testing when upgrading the SDK if you are using a mode other than
# `'legacy'`. While the `'legacy'` defaults mode is specific to Ruby,
# other modes are standardized across all of the AWS SDKs.
#
# The defaults mode can be configured:
#
# * Directly on a client via `:defaults_mode`
#
# * On a configuration profile via the "defaults_mode" profile file property.
#
# * Globally via the "AWS_DEFAULTS_MODE" environment variable.
#
#
# @code_generation START - documentation
# The following `:default_mode` values are supported:
#
# * `'standard'` -
# The STANDARD mode provides the latest recommended default values
# that should be safe to run in most scenarios
#
# Note that the default values vended from this mode might change as
# best practices may evolve. As a result, it is encouraged to perform
# tests when upgrading the SDK
#
# * `'in-region'` -
# The IN\_REGION mode builds on the standard mode and includes
# optimization tailored for applications which call AWS services from
# within the same AWS region
#
# Note that the default values vended from this mode might change as
# best practices may evolve. As a result, it is encouraged to perform
# tests when upgrading the SDK
#
# * `'cross-region'` -
# The CROSS\_REGION mode builds on the standard mode and includes
# optimization tailored for applications which call AWS services in a
# different region
#
# Note that the default values vended from this mode might change as
# best practices may evolve. As a result, it is encouraged to perform
# tests when upgrading the SDK
#
# * `'mobile'` -
# The MOBILE mode builds on the standard mode and includes
# optimization tailored for mobile applications
#
# Note that the default values vended from this mode might change as
# best practices may evolve. As a result, it is encouraged to perform
# tests when upgrading the SDK
#
# * `'auto'` -
# The AUTO mode is an experimental mode that builds on the standard
# mode. The SDK will attempt to discover the execution environment to
# determine the appropriate settings automatically.
#
# Note that the auto detection is heuristics-based and does not
# guarantee 100% accuracy. STANDARD mode will be used if the execution
# environment cannot be determined. The auto detection might query
# [EC2 Instance Metadata service][1], which might introduce latency.
# Therefore we recommend choosing an explicit defaults\_mode instead
# if startup latency is critical to your application
#
# * `'legacy'` -
# The LEGACY mode provides default settings that vary per SDK and were
# used prior to establishment of defaults\_mode
#
# Based on the provided mode, the SDK will vend sensible default values
# tailored to the mode for the following settings:
#
# * `:retry_mode` -
# A retry mode specifies how the SDK attempts retries. See [Retry
# Mode][2]
#
# * `:sts_regional_endpoints` -
# Specifies how the SDK determines the AWS service endpoint that it
# uses to talk to the AWS Security Token Service (AWS STS). See
# [Setting STS Regional endpoints][3]
#
# * `:s3_us_east_1_regional_endpoint` -
# Specifies how the SDK determines the AWS service endpoint that it
# uses to talk to the Amazon S3 for the us-east-1 region
#
# * `:http_open_timeout` -
# The amount of time after making an initial connection attempt on a
# socket, where if the client does not receive a completion of the
# connect handshake, the client gives up and fails the operation
#
# * `:ssl_timeout` -
# The maximum amount of time that a TLS handshake is allowed to take
# from the time the CLIENT HELLO message is sent to ethe time the
# client and server have fully negotiated ciphers and exchanged keys
#
# All options above can be configured by users, and the overridden value will take precedence.
#
# [1]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
# [2]: https://docs.aws.amazon.com/sdkref/latest/guide/setting-global-retry_mode.html
# [3]: https://docs.aws.amazon.com/sdkref/latest/guide/setting-global-sts_regional_endpoints.html
#
# @code_generation END - documentation
module DefaultsModeConfiguration
# @api private
# @code_generation START - configuration
SDK_DEFAULT_CONFIGURATION =
{
"version" => 1,
"base" => {
"retryMode" => "standard",
"stsRegionalEndpoints" => "regional",
"s3UsEast1RegionalEndpoints" => "regional",
"connectTimeoutInMillis" => 1100,
"tlsNegotiationTimeoutInMillis" => 1100
},
"modes" => {
"standard" => {
"connectTimeoutInMillis" => {
"override" => 3100
},
"tlsNegotiationTimeoutInMillis" => {
"override" => 3100
}
},
"in-region" => {
},
"cross-region" => {
"connectTimeoutInMillis" => {
"override" => 3100
},
"tlsNegotiationTimeoutInMillis" => {
"override" => 3100
}
},
"mobile" => {
"connectTimeoutInMillis" => {
"override" => 30000
},
"tlsNegotiationTimeoutInMillis" => {
"override" => 30000
}
}
}
}
# @code_generation END - configuration
end
end
107 changes: 107 additions & 0 deletions gems/aws-sdk-core/lib/aws-defaults/defaults_mode_config_resolver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# frozen_string_literal: true

module Aws
#@api private
class DefaultsModeConfigResolver

@@application_region = nil
@@application_region_mutex = Mutex.new
@@imds_client = EC2Metadata.new(retries: 0, http_open_timeout: 0.01)

# mappings from Ruby SDK configuration names to the
# sdk defaults option names and (optional) scale modifiers
CFG_OPTIONS = {
retry_mode: { name: "retryMode" },
sts_regional_endpoints: { name: "stsRegionalEndpoints" },
s3_us_east_1_regional_endpoint: { name: "s3UsEast1RegionalEndpoints" },
http_open_timeout: { name: "connectTimeoutInMillis", scale: 0.001 },
http_read_timeout: { name: "timeToFirstByteTimeoutInMillis", scale: 0.001 },
ssl_timeout: { name: "tlsNegotiationTimeoutInMillis", scale: 0.001 }
}.freeze

def initialize(sdk_defaults, cfg)
@sdk_defaults = sdk_defaults
@cfg = cfg
@resolved_mode = nil
@mutex = Mutex.new
end

# option_name should be the symbolized ruby name to resolve
# returns the ruby appropriate value or nil if none are resolved
def resolve(option_name)
return unless (std_option = CFG_OPTIONS[option_name])
mode = resolved_mode.downcase

return nil if mode == 'legacy'

value = resolve_for_mode(std_option[:name], mode)
value = value * std_option[:scale] if value && std_option[:scale]

value
end

private
def resolved_mode
@mutex.synchronize do
return @resolved_mode unless @resolved_mode.nil?

@resolved_mode = @cfg.defaults_mode == 'auto' ? resolve_auto_mode : @cfg.defaults_mode
end
end

def resolve_auto_mode
return "mobile" if env_mobile?

region = application_current_region

if region
@cfg.region == region ? "in-region": "cross-region"
else
# We don't seem to be mobile, and we couldn't determine whether we're running within an AWS region. Fall back to standard.
'standard'
end
end

def application_current_region
resolved_region = @@application_region_mutex.synchronize do
return @@application_region unless @@application_region.nil?

region = nil
if ENV['AWS_EXECUTION_ENV']
region = ENV['AWS_REGION'] || ENV['AWS_DEFAULT_REGION']
end

if region.nil? && ENV['AWS_EC2_METADATA_DISABLED']&.downcase != "true"
begin
region = @@imds_client.get('/latest/meta-data/placement/region')
rescue
# unable to get region, leave it unset
end
end

# required so that we cache the unknown/nil result
@@application_region = region || :unknown
end
resolved_region == :unknown ? nil : resolved_region
end

def resolve_for_mode(name, mode)
base_value = @sdk_defaults['base'][name]
mode_value = @sdk_defaults['modes'].fetch(mode, {})[name]

if mode_value.nil?
return base_value
end

return mode_value['override'] unless mode_value['override'].nil?
return base_value + mode_value['add'] unless mode_value['add'].nil?
return base_value * mode_value['multiply'] unless mode_value['multiply'].nil?
return base_value
end

def env_mobile?
false
end

end
end
3 changes: 3 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
require_relative 'aws-sdk-core/arn_parser'
require_relative 'aws-sdk-core/ec2_metadata'

# defaults
require_relative 'aws-defaults'

# plugins
# loaded through building STS or SSO ..

Expand Down
40 changes: 40 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/plugins/defaults_mode.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

module Aws
# @api private
module Plugins
# @api private
class DefaultsMode < Seahorse::Client::Plugin

option(:defaults_mode,
default: 'legacy',
doc_type: String,
docstring: <<-DOCS
See {Aws::DefaultsModeConfiguration} for a list of the
accepted modes and the configuration defaults that are included.
DOCS
) do |cfg|
resolve_defaults_mode(cfg)
end

option(:defaults_mode_config_resolver,
doc_type: 'Aws::DefaultsModeConfigResolver') do |cfg|
Aws::DefaultsModeConfigResolver.new(
Aws::DefaultsModeConfiguration::SDK_DEFAULT_CONFIGURATION, cfg)
end

class << self
private

def resolve_defaults_mode(cfg)
value = ENV['AWS_DEFAULTS_MODE']
value ||= Aws.shared_config.defaults_mode(
profile: cfg.profile
)
value&.downcase || "legacy"
end
end

end
end
end
12 changes: 9 additions & 3 deletions gems/aws-sdk-core/lib/aws-sdk-core/plugins/retry_errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,15 @@ class RetryErrors < Seahorse::Client::Plugin
option(:clock_skew) { Retries::ClockSkew.new }

def self.resolve_retry_mode(cfg)
value = ENV['AWS_RETRY_MODE'] ||
Aws.shared_config.retry_mode(profile: cfg.profile) ||
'legacy'
default_mode_value =
if cfg.respond_to?(:defaults_mode_config_resolver)
cfg.defaults_mode_config_resolver.resolve(:retry_mode)
end

value = ENV['AWS_RETRY_MODE'] ||
Aws.shared_config.retry_mode(profile: cfg.profile) ||
default_mode_value ||
'legacy'
# Raise if provided value is not one of the retry modes
if value != 'legacy' && value != 'standard' && value != 'adaptive'
raise ArgumentError,
Expand Down

0 comments on commit 378dfa6

Please sign in to comment.