aws_advanced_ruby_driver_wrapper 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE +175 -0
- data/NOTICE +1 -0
- data/README.md +168 -0
- data/THIRD-PARTY-LICENSES +473 -0
- data/aws_advanced_ruby_driver_wrapper.gemspec +73 -0
- data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_mysql2_adapter.rb +73 -0
- data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_postgresql_adapter.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/custom_configuration.rb +58 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_mysql_dialect.rb +103 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_pg_dialect.rb +124 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/dialect_codes.rb +38 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_mysql_dialect.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_pg_dialect.rb +92 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_mysql_dialect.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_pg_dialect.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/mysql_dialect.rb +98 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/pg_dialect.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_mysql_dialect.rb +88 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_pg_dialect.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/unknown_dialect.rb +72 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/utils/dialect_utils.rb +71 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect.rb +154 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect_manager.rb +55 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/mysql_driver_dialect.rb +165 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/pg_driver_dialect.rb +201 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/error_handler.rb +62 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/mysql_error_handler.rb +80 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/pg_error_handler.rb +126 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors.rb +59 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/connection_string_host_list_provider.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/global_aurora_host_list_provider.rb +65 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability.rb +24 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability_strategy.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_info.rb +137 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_role.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/random_host_selector.rb +40 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/rds_host_list_provider.rb +206 -0
- data/lib/aws_advanced_ruby_driver_wrapper/logging.rb +110 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/cluster_topology_monitor.rb +709 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/global_cluster_topology_monitor.rb +72 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_connection.rb +57 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_state.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/mysql.rb +429 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/blue_green_plugin.rb +205 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/host_mapper.rb +132 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/iam_host_tracker.rb +84 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interim_status.rb +92 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interval_rate.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase.rb +69 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_event_log.rb +85 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_time_info.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/role.rb +38 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/base_routing.rb +83 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/reject_connect_routing.rb +40 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/substitute_connect_routing.rb +136 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_connect_routing.rb +53 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_execute_routing.rb +52 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_until_corresponding_host_found_connect_routing.rb +83 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status.rb +68 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_builder.rb +244 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_info.rb +30 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_monitor.rb +564 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_provider.rb +414 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_state.rb +98 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_timer.rb +46 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_monitor.rb +266 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_plugin.rb +158 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/info.rb +111 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/member_list_type.rb +31 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/role.rb +45 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/default_plugin.rb +108 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_mode.rb +43 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_plugin.rb +467 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_mode.rb +68 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_plugin.rb +403 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/iam_auth_plugin.rb +159 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/initial_connection_strategy_plugin.rb +485 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/audit_logger.rb +157 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_cipher.rb +159 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_encryption_config.rb +61 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/connection_source.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/data_key_cache.rb +220 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_algorithm.rb +75 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_config.rb +146 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_service.rb +391 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/error_context.rb +198 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/errors.rb +259 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_management_utility.rb +435 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_manager.rb +378 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_metadata.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_plugin.rb +890 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_utility.rb +281 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/metadata_manager.rb +332 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sanitizer.rb +147 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_name.rb +70 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_validator.rb +211 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sql_runner.rb +147 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/type_marker.rb +109 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/secrets_manager_plugin.rb +358 -0
- data/lib/aws_advanced_ruby_driver_wrapper/postgresql.rb +659 -0
- data/lib/aws_advanced_ruby_driver_wrapper/property_definition.rb +409 -0
- data/lib/aws_advanced_ruby_driver_wrapper/ruby_method.rb +122 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/connection_service.rb +143 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/dialect_service.rb +267 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/host_service.rb +199 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/monitor_service.rb +186 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_call_context.rb +63 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_manager.rb +273 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/service_container.rb +30 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/service_utility.rb +78 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/session_state_service.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/accessible_regions.rb +52 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/ar_constants.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/aurora_topology_utils.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/aws_credentials_utils.rb +62 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config_parser.rb +368 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/conversion_utils.rb +51 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/batching_event_publisher.rb +119 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/data_access_event.rb +26 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/monitor_reset_event.rb +26 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/global_aurora_topology_utils.rb +185 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/host_list_utils.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/iam_auth_utils.rb +112 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/multi_az_topology_utils.rb +117 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/encryption_annotation_parser.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/mysql_statement_analyzer.rb +641 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/pg_statement_analyzer.rb +502 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_analysis.rb +63 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_type.rb +35 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint_parser.rb +50 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/sql_parser.rb +139 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_url_type.rb +71 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_utils.rb +575 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/retry_util.rb +153 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_encoding.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_method_analyzer.rb +195 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/cache_entry.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/expiration_cache.rb +108 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/sliding_expiration_cache.rb +137 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/storage_service.rb +172 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/topology_utils.rb +127 -0
- data/lib/aws_advanced_ruby_driver_wrapper/version.rb +19 -0
- data/lib/aws_advanced_ruby_driver_wrapper/wrapper_property.rb +64 -0
- data/lib/aws_advanced_ruby_driver_wrapper.rb +116 -0
- metadata +227 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
|
6
|
+
# You may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require_relative '../failover_plugin'
|
|
18
|
+
require_relative '../../errors'
|
|
19
|
+
require_relative '../../host/host_role'
|
|
20
|
+
require_relative '../../property_definition'
|
|
21
|
+
require_relative '../../utils/accessible_regions'
|
|
22
|
+
require_relative '../../utils/rds_utils'
|
|
23
|
+
require_relative '../../utils/rds_url_type'
|
|
24
|
+
require_relative 'gdb_failover_mode'
|
|
25
|
+
|
|
26
|
+
module AwsAdvancedRubyDriverWrapper
|
|
27
|
+
module Plugins
|
|
28
|
+
module Gdb
|
|
29
|
+
# Failover plugin for Global Aurora Databases.
|
|
30
|
+
#
|
|
31
|
+
# Unlike {FailoverPlugin}, which always targets the same role, this plugin picks its target
|
|
32
|
+
# based on the region of the cluster that is currently the GDB primary. Two modes are
|
|
33
|
+
# configured: +in_home_failover_mode+ applies while the primary region is the home region,
|
|
34
|
+
# and +out_of_home_failover_mode+ applies while it is not. Both accept any of the modes in
|
|
35
|
+
# {GdbFailoverMode}.
|
|
36
|
+
#
|
|
37
|
+
# The home region is taken from +failover_home_region+, or derived from the initial endpoint
|
|
38
|
+
# when that endpoint carries a region. When +accessible_regions+ is set, hosts outside those
|
|
39
|
+
# regions are never selected.
|
|
40
|
+
class GdbFailoverPlugin < FailoverPlugin
|
|
41
|
+
def initialize(service_container, props = ::Concurrent::Map.new)
|
|
42
|
+
super
|
|
43
|
+
|
|
44
|
+
# The inherited @failover_mode is unused in this class; @in_home_failover_mode and
|
|
45
|
+
# @out_of_home_failover_mode are consulted instead.
|
|
46
|
+
@in_home_failover_mode = nil
|
|
47
|
+
@out_of_home_failover_mode = nil
|
|
48
|
+
@home_region = nil
|
|
49
|
+
@accessible_regions = nil
|
|
50
|
+
# Hosts whose region could not be determined, so that each is only logged once per failover.
|
|
51
|
+
@regionless_hosts = Set.new
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def init_failover_mode
|
|
57
|
+
return unless @rds_url_type.nil?
|
|
58
|
+
|
|
59
|
+
initial_host = connection_service.initial_host_info
|
|
60
|
+
@rds_url_type = Utils::RdsUtils.identify_rds_type(initial_host&.host)
|
|
61
|
+
reject_rds_proxy_endpoint
|
|
62
|
+
|
|
63
|
+
@home_region = resolve_home_region(initial_host)
|
|
64
|
+
@accessible_regions = Utils::AccessibleRegions.parse(@wrapper_props)
|
|
65
|
+
|
|
66
|
+
if @accessible_regions && !@accessible_regions.include?(@home_region.downcase)
|
|
67
|
+
raise Errors::AwsError,
|
|
68
|
+
"Home region '#{@home_region}' is not included in the list of accessible regions " \
|
|
69
|
+
"#{@accessible_regions.to_a}. The home region must be accessible."
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
@in_home_failover_mode = resolve_failover_mode(PropertyDefinition::IN_HOME_FAILOVER_MODE)
|
|
73
|
+
@out_of_home_failover_mode = resolve_failover_mode(PropertyDefinition::OUT_OF_HOME_FAILOVER_MODE)
|
|
74
|
+
|
|
75
|
+
logger.debug do
|
|
76
|
+
"failover_home_region=#{@home_region}, accessible_regions=#{@accessible_regions&.to_a}, " \
|
|
77
|
+
"in_home_failover_mode=#{@in_home_failover_mode}, " \
|
|
78
|
+
"out_of_home_failover_mode=#{@out_of_home_failover_mode}"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @return [String] the configured home region, or the region of the initial endpoint
|
|
83
|
+
# @raise [Errors::AwsError] if no home region is configured and none can be derived
|
|
84
|
+
def resolve_home_region(initial_host)
|
|
85
|
+
configured = PropertyDefinition::FAILOVER_HOME_REGION.get_string(@wrapper_props)
|
|
86
|
+
unless configured.nil? || configured.strip.empty?
|
|
87
|
+
normalized = configured.strip
|
|
88
|
+
unless Utils::RdsUtils.valid_region?(normalized)
|
|
89
|
+
raise Errors::AwsError,
|
|
90
|
+
"#{PropertyDefinition::FAILOVER_HOME_REGION.name} '#{configured}' is an unknown or misspelled AWS region"
|
|
91
|
+
end
|
|
92
|
+
return normalized
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
derived = @rds_url_type&.region? ? Utils::RdsUtils.rds_region(initial_host&.host) : nil
|
|
96
|
+
if derived.nil? || derived.empty?
|
|
97
|
+
raise Errors::AwsError,
|
|
98
|
+
"Unable to determine region from endpoint #{initial_host&.host}. If you are connecting via a global database " \
|
|
99
|
+
"endpoint or non-standard URL, please set the #{PropertyDefinition::FAILOVER_HOME_REGION.name} property."
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
derived
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Resolves one of the two configured modes, defaulting based on the initial endpoint type.
|
|
106
|
+
#
|
|
107
|
+
# @param property [WrapperProperty]
|
|
108
|
+
# @return [Symbol] one of the {GdbFailoverMode} constants
|
|
109
|
+
def resolve_failover_mode(property)
|
|
110
|
+
configured = GdbFailoverMode.from_value(property.get_string(@wrapper_props))
|
|
111
|
+
return configured unless configured.nil?
|
|
112
|
+
|
|
113
|
+
if [Utils::RdsUrlType::RDS_WRITER_CLUSTER, Utils::RdsUrlType::RDS_GLOBAL_WRITER_CLUSTER].include?(@rds_url_type)
|
|
114
|
+
GdbFailoverMode::STRICT_WRITER
|
|
115
|
+
else
|
|
116
|
+
GdbFailoverMode::HOME_READER_OR_WRITER
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Whether a read-only error should trigger failover, i.e. whether strict_writer is the mode
|
|
121
|
+
# that currently applies.
|
|
122
|
+
#
|
|
123
|
+
# Which of the two configured modes applies depends on the region of the GDB primary, which is
|
|
124
|
+
# not known yet: a read-only error is a sign that the primary has changed, so the latest known
|
|
125
|
+
# topology may no longer say where it is. When only one of the modes is +strict_writer+ that mode
|
|
126
|
+
# is assumed, so that a connection that has become read-only is not left as is. {#failover}
|
|
127
|
+
# resolves the mode again once the new primary is known.
|
|
128
|
+
#
|
|
129
|
+
# @return [Boolean]
|
|
130
|
+
def failover_on_read_only_error?
|
|
131
|
+
in_home_strict_writer = @in_home_failover_mode == GdbFailoverMode::STRICT_WRITER
|
|
132
|
+
out_of_home_strict_writer = @out_of_home_failover_mode == GdbFailoverMode::STRICT_WRITER
|
|
133
|
+
return in_home_strict_writer if in_home_strict_writer == out_of_home_strict_writer
|
|
134
|
+
|
|
135
|
+
logger.debug do
|
|
136
|
+
'A read-only error was encountered. The region of the GDB primary is not known yet, but the error suggests ' \
|
|
137
|
+
'the primary may have changed, so driver failover will be triggered.'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
true
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# The mode that applies given the region of the current GDB primary.
|
|
144
|
+
#
|
|
145
|
+
# @param primary_region [String] the region the primary writer is in. Must not be nil - callers are
|
|
146
|
+
# responsible for handling endpoints whose region cannot be determined.
|
|
147
|
+
# @return [Symbol] one of the {GdbFailoverMode} constants
|
|
148
|
+
def current_failover_mode(primary_region)
|
|
149
|
+
home_region?(primary_region) ? @in_home_failover_mode : @out_of_home_failover_mode
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Whether the given region is the home region.
|
|
153
|
+
#
|
|
154
|
+
# @param region [String] must not be nil; callers are responsible for handling endpoints
|
|
155
|
+
# whose region cannot be determined.
|
|
156
|
+
# @return [Boolean]
|
|
157
|
+
def home_region?(region)
|
|
158
|
+
@home_region.casecmp?(region)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Whether a host is in one of the accessible regions. All hosts are accessible when no
|
|
162
|
+
# region restriction is configured.
|
|
163
|
+
#
|
|
164
|
+
# @param host_info [Host::HostInfo]
|
|
165
|
+
# @return [Boolean]
|
|
166
|
+
def accessible_region?(host_info)
|
|
167
|
+
return true if @accessible_regions.nil?
|
|
168
|
+
|
|
169
|
+
region = Utils::RdsUtils.rds_region(host_info.host)
|
|
170
|
+
!region.nil? && @accessible_regions.include?(region.downcase)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def failover
|
|
174
|
+
if @closed_explicitly
|
|
175
|
+
logger.debug { 'Connection was explicitly closed, skipping failover' }
|
|
176
|
+
return
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
failover_start = Time.now
|
|
180
|
+
failover_deadline = failover_start + @failover_timeout
|
|
181
|
+
@regionless_hosts.clear
|
|
182
|
+
|
|
183
|
+
logger.info do
|
|
184
|
+
"Starting global database failover from #{connection_service.current_host_info&.url || 'an unknown host'}. " \
|
|
185
|
+
"accessible_regions=#{@accessible_regions.nil? ? 'all' : @accessible_regions.to_a}, " \
|
|
186
|
+
"in_home_failover_mode=#{@in_home_failover_mode}, " \
|
|
187
|
+
"out_of_home_failover_mode=#{@out_of_home_failover_mode}, " \
|
|
188
|
+
"reader_host_selector_strategy=#{@reader_selector_strategy}, " \
|
|
189
|
+
"failover_timeout_sec=#{@failover_timeout}"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# This is expected to return once the topology has stabilized, i.e. once the cluster
|
|
193
|
+
# control plane has already chosen a new writer.
|
|
194
|
+
unless host_service.force_refresh_host_list?(verify_writer: true, timeout_sec: @failover_timeout)
|
|
195
|
+
raise Errors::FailoverFailedError, 'The request to discover the new topology timed out or was unsuccessful'
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
writer_candidate = host_service.all_hosts.find { |h| h.role == Host::HostRole::WRITER }
|
|
199
|
+
if writer_candidate.nil?
|
|
200
|
+
raise Errors::FailoverFailedError,
|
|
201
|
+
"Unable to find a writer in the updated host list: #{host_service.all_hosts.map(&:url)}"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
writer_region = Utils::RdsUtils.rds_region(writer_candidate.host)
|
|
205
|
+
if writer_region.nil? || writer_region.empty?
|
|
206
|
+
# Unable to determine whether the writer is in-home or out-of-home. The writer usually stays in the same region during failover,
|
|
207
|
+
# so we will assume the user connected to the home region and the writer stayed in-home.
|
|
208
|
+
mode = @in_home_failover_mode
|
|
209
|
+
else
|
|
210
|
+
mode = current_failover_mode(writer_region)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
log_failover_plan(writer_candidate, writer_region, mode)
|
|
214
|
+
if mode == GdbFailoverMode::STRICT_WRITER
|
|
215
|
+
failover_to_writer(writer_candidate, writer_region, failover_deadline)
|
|
216
|
+
else
|
|
217
|
+
failover_to_allowed_host(mode, failover_deadline)
|
|
218
|
+
end
|
|
219
|
+
ensure
|
|
220
|
+
duration_ms = ((Time.now - failover_start) * 1000).round if failover_start
|
|
221
|
+
logger.debug { "Failover duration: #{duration_ms}ms" } if duration_ms
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Explains which mode was chosen, and what the plugin will do with it.
|
|
225
|
+
#
|
|
226
|
+
# @param writer_candidate [Host::HostInfo] the writer the topology settled on
|
|
227
|
+
# @param writer_region [String, nil] the region the new writer is in
|
|
228
|
+
# @param mode [Symbol] one of the {GdbFailoverMode} constants
|
|
229
|
+
def log_failover_plan(writer_candidate, writer_region, mode)
|
|
230
|
+
if writer_region.nil?
|
|
231
|
+
# Hosts in a GDB topology are built from the region-prefixed instance patterns in
|
|
232
|
+
# global_cluster_instance_host_patterns, so they normally always carry a parseable
|
|
233
|
+
# region. A writer that does not is a sign of a misconfigured pattern.
|
|
234
|
+
logger.warn do
|
|
235
|
+
"Unable to determine region of writer #{writer_candidate.host}. Please ensure you have set the " \
|
|
236
|
+
"#{PropertyDefinition::GLOBAL_CLUSTER_INSTANCE_HOST_PATTERNS.name} setting. Failover will assume " \
|
|
237
|
+
"in-home failover mode #{@in_home_failover_mode}."
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
return
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
logger.info do
|
|
244
|
+
primary = if home_region?(writer_region)
|
|
245
|
+
"The GDB primary is now #{writer_candidate.url}, which is in the home region " \
|
|
246
|
+
"'#{@home_region}'. Using in_home_failover_mode=#{mode}."
|
|
247
|
+
else
|
|
248
|
+
"The GDB primary is now #{writer_candidate.url}, which is in region '#{writer_region}' " \
|
|
249
|
+
"rather than the home region '#{@home_region}'. Using out_of_home_failover_mode=#{mode}."
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
"#{primary} #{failover_target_description(mode)}"
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# A plain-language description of the hosts the given mode will target.
|
|
257
|
+
#
|
|
258
|
+
# @param mode [Symbol] one of the {GdbFailoverMode} constants
|
|
259
|
+
# @return [String]
|
|
260
|
+
def failover_target_description(mode)
|
|
261
|
+
case mode
|
|
262
|
+
when GdbFailoverMode::STRICT_WRITER then 'Connecting to the new writer.'
|
|
263
|
+
when GdbFailoverMode::STRICT_HOME_READER then "Connecting to a reader in the home region '#{@home_region}'."
|
|
264
|
+
when GdbFailoverMode::STRICT_OUT_OF_HOME_READER then "Connecting to a reader outside the home region '#{@home_region}'."
|
|
265
|
+
when GdbFailoverMode::STRICT_ANY_READER then 'Connecting to a reader in any region.'
|
|
266
|
+
when GdbFailoverMode::HOME_READER_OR_WRITER then "Connecting to the writer or a reader in the home region '#{@home_region}'."
|
|
267
|
+
when GdbFailoverMode::OUT_OF_HOME_READER_OR_WRITER
|
|
268
|
+
"Connecting to the writer or a reader outside the home region '#{@home_region}'."
|
|
269
|
+
when GdbFailoverMode::ANY_READER_OR_WRITER then 'Connecting to the writer or a reader in any region.'
|
|
270
|
+
else "Connecting to a host allowed by failover mode #{mode}."
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def failover_to_writer(writer_candidate, writer_region, deadline)
|
|
275
|
+
unless accessible_region?(writer_candidate)
|
|
276
|
+
raise Errors::FailoverFailedError,
|
|
277
|
+
"Writer is in region '#{writer_region}' which is not in the list of accessible regions " \
|
|
278
|
+
"#{@accessible_regions.to_a}."
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
was_in_transaction = @service_container.session_state_service.in_transaction?
|
|
282
|
+
result = nil
|
|
283
|
+
success = false
|
|
284
|
+
|
|
285
|
+
begin
|
|
286
|
+
result = @retry_util.connect_to_writer(self, @service_container.plugin_manager, deadline: deadline)
|
|
287
|
+
if result&.connection && result.host_info
|
|
288
|
+
success = true
|
|
289
|
+
connection_service.update_current_connection(result.connection, result.host_info)
|
|
290
|
+
raise_failover_success_error(was_in_transaction)
|
|
291
|
+
end
|
|
292
|
+
rescue Timeout::Error
|
|
293
|
+
raise Errors::FailoverFailedError,
|
|
294
|
+
"Failover timed out after #{@failover_timeout}s. " \
|
|
295
|
+
"Unable to connect to the new writer #{writer_candidate.host}."
|
|
296
|
+
ensure
|
|
297
|
+
close_quietly(result&.connection) unless success
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def failover_to_allowed_host(mode, deadline)
|
|
302
|
+
was_in_transaction = @service_container.session_state_service.in_transaction?
|
|
303
|
+
result = nil
|
|
304
|
+
success = false
|
|
305
|
+
|
|
306
|
+
begin
|
|
307
|
+
result = @retry_util.connect_to_allowed_host(
|
|
308
|
+
self,
|
|
309
|
+
@service_container.plugin_manager,
|
|
310
|
+
verify_role: verify_role_for(mode),
|
|
311
|
+
strategy: @reader_selector_strategy,
|
|
312
|
+
deadline: deadline
|
|
313
|
+
) { |allowed_hosts| allowed_hosts_for(mode, allowed_hosts) }
|
|
314
|
+
if result&.connection && result.host_info
|
|
315
|
+
success = true
|
|
316
|
+
connection_service.update_current_connection(result.connection, result.host_info)
|
|
317
|
+
raise_failover_success_error(was_in_transaction)
|
|
318
|
+
end
|
|
319
|
+
rescue Timeout::Error
|
|
320
|
+
raise Errors::FailoverFailedError,
|
|
321
|
+
"Failover timed out after #{@failover_timeout}s. Unable to connect to a host allowed by failover mode #{mode}."
|
|
322
|
+
ensure
|
|
323
|
+
close_quietly(result&.connection) unless success
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# The role a new connection must report, or nil when either role is acceptable.
|
|
328
|
+
#
|
|
329
|
+
# @param mode [Symbol] one of the {GdbFailoverMode} constants
|
|
330
|
+
# @return [Symbol, nil]
|
|
331
|
+
def verify_role_for(mode)
|
|
332
|
+
case mode
|
|
333
|
+
when GdbFailoverMode::STRICT_HOME_READER,
|
|
334
|
+
GdbFailoverMode::STRICT_OUT_OF_HOME_READER,
|
|
335
|
+
GdbFailoverMode::STRICT_ANY_READER
|
|
336
|
+
Host::HostRole::READER
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# The hosts that may be connected to under the given mode. Called on every retry with the
|
|
341
|
+
# allowed hosts from the latest topology refresh.
|
|
342
|
+
#
|
|
343
|
+
# @param mode [Symbol] one of the {GdbFailoverMode} constants
|
|
344
|
+
# @param allowed_hosts [Array<Host::HostInfo>] the current allowed hosts
|
|
345
|
+
# @return [Array<Host::HostInfo>]
|
|
346
|
+
def allowed_hosts_for(mode, allowed_hosts)
|
|
347
|
+
hosts = allowed_hosts.select { |host| host_allowed?(host, mode) }
|
|
348
|
+
hosts.select { |host| accessible_region?(host) }
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def host_allowed?(host, mode)
|
|
352
|
+
reader = host.role == Host::HostRole::READER
|
|
353
|
+
writer = host.role == Host::HostRole::WRITER
|
|
354
|
+
|
|
355
|
+
case mode
|
|
356
|
+
when GdbFailoverMode::STRICT_HOME_READER then reader && region_position(host) == :in_home
|
|
357
|
+
when GdbFailoverMode::STRICT_OUT_OF_HOME_READER then reader && region_position(host) == :out_of_home
|
|
358
|
+
when GdbFailoverMode::STRICT_ANY_READER then reader
|
|
359
|
+
when GdbFailoverMode::HOME_READER_OR_WRITER then writer || (reader && region_position(host) == :in_home)
|
|
360
|
+
when GdbFailoverMode::OUT_OF_HOME_READER_OR_WRITER then writer || (reader && region_position(host) == :out_of_home)
|
|
361
|
+
when GdbFailoverMode::ANY_READER_OR_WRITER then true
|
|
362
|
+
else raise Errors::AwsError, "Unsupported global database failover mode: #{mode}"
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Where the given host sits relative to the home region. Only consulted for modes that place
|
|
367
|
+
# a region requirement on the host; +strict_any_reader+ and +any_reader_or_writer+ accept a
|
|
368
|
+
# host regardless of its region and so never call this.
|
|
369
|
+
#
|
|
370
|
+
# @param host [Host::HostInfo]
|
|
371
|
+
# @return [Symbol, nil] +:in_home+, +:out_of_home+, or nil if the region could not be determined
|
|
372
|
+
def region_position(host)
|
|
373
|
+
region = Utils::RdsUtils.rds_region(host.host)
|
|
374
|
+
if region.nil? || region.empty?
|
|
375
|
+
# This scenario is not expected: topology hosts are built from the region-prefixed instance patterns in
|
|
376
|
+
# global_cluster_instance_host_patterns, so they should always carry a parseable region. Without one the
|
|
377
|
+
# configured mode cannot be honoured for this host, so it is skipped. Only the first occurrence is logged,
|
|
378
|
+
# since this method is called on every failover retry and the reason for the failure does not change.
|
|
379
|
+
if @regionless_hosts.add?(host.host)
|
|
380
|
+
logger.debug do
|
|
381
|
+
"Unable to determine the region of #{host.host}, so it will not be considered an allowed host for the " \
|
|
382
|
+
'configured failover mode.'
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
return nil
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
home_region?(region) ? :in_home : :out_of_home
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# @raise [NotImplementedError] always; see {#failover} for this plugin's implementation
|
|
392
|
+
def failover_reader
|
|
393
|
+
raise NotImplementedError, "#{self.class} performs failover via #failover"
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# @raise [NotImplementedError] always; see {#failover} for this plugin's implementation
|
|
397
|
+
def failover_writer
|
|
398
|
+
raise NotImplementedError, "#{self.class} performs failover via #failover"
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
|
6
|
+
# You may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require 'concurrent'
|
|
18
|
+
require_relative '../errors'
|
|
19
|
+
require_relative '../utils/aws_credentials_utils'
|
|
20
|
+
require_relative '../utils/iam_auth_utils'
|
|
21
|
+
require_relative '../utils/rds_utils'
|
|
22
|
+
require_relative '../utils/rds_url_type'
|
|
23
|
+
require_relative '../driver_dialects/driver_dialect_manager'
|
|
24
|
+
require_relative '../property_definition'
|
|
25
|
+
|
|
26
|
+
module AwsAdvancedRubyDriverWrapper
|
|
27
|
+
module Plugins
|
|
28
|
+
class IamAuthPlugin
|
|
29
|
+
SUBSCRIBED_METHODS = Set['connect', 'internal_connect'].freeze
|
|
30
|
+
IAM_TOKEN_CACHE_NAME = :iam_token
|
|
31
|
+
|
|
32
|
+
attr_reader :subscribed_methods
|
|
33
|
+
|
|
34
|
+
def initialize(service_container, props = ::Concurrent::Map.new)
|
|
35
|
+
ensure_aws_sdk!
|
|
36
|
+
@service_container = service_container
|
|
37
|
+
@wrapper_props = props
|
|
38
|
+
@credentials_provider = PropertyDefinition::AWS_CREDENTIALS_PROVIDER.get(props) ||
|
|
39
|
+
Aws::CredentialProviderChain.new.resolve
|
|
40
|
+
expiration = PropertyDefinition::IAM_EXPIRATION_SEC.get_float(props)
|
|
41
|
+
PropertyDefinition::IAM_EXPIRATION_SEC.validate!(expiration)
|
|
42
|
+
service_container.storage_service.register(IAM_TOKEN_CACHE_NAME, ttl: expiration)
|
|
43
|
+
@subscribed_methods = SUBSCRIBED_METHODS
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def connect(host_info, driver_props, _is_initial_connection, pipeline_callable)
|
|
47
|
+
iam_connect(host_info, driver_props, pipeline_callable)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def internal_connect(host_info, driver_props, wrapper_props_override, _is_initial_connection, pipeline_callable)
|
|
51
|
+
iam_connect(host_info, driver_props, pipeline_callable, wrapper_props_override || @wrapper_props)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.clear_cache(storage_service)
|
|
55
|
+
storage_service.clear(IAM_TOKEN_CACHE_NAME)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def iam_connect(host_info, driver_props, pipeline_callable, wrapper_props_override = nil)
|
|
61
|
+
wrapper_props_override ||= @wrapper_props
|
|
62
|
+
|
|
63
|
+
user = driver_props[:user] || driver_props[:username]
|
|
64
|
+
raise Errors::IamAuthError, 'IamAuthPlugin: :user is required' if user.nil? || user.empty?
|
|
65
|
+
|
|
66
|
+
host = Utils::IamAuthUtils.resolve_host(
|
|
67
|
+
PropertyDefinition::IAM_HOST.get(wrapper_props_override), host_info
|
|
68
|
+
)
|
|
69
|
+
rds_type = Utils::RdsUtils.identify_rds_type(host)
|
|
70
|
+
begin
|
|
71
|
+
region = Utils::IamAuthUtils.region_for(
|
|
72
|
+
host:, props: wrapper_props_override, rds_type:, credentials_provider: @credentials_provider,
|
|
73
|
+
rds_client_func: -> { rds_client }
|
|
74
|
+
)
|
|
75
|
+
rescue Aws::Errors::MissingRegionError
|
|
76
|
+
raise Errors::IamAuthError,
|
|
77
|
+
'IamAuthPlugin: unable to determine connection region. ' \
|
|
78
|
+
"If you are using a non-standard RDS URL, please set the 'iam_region' property."
|
|
79
|
+
end
|
|
80
|
+
unless region
|
|
81
|
+
raise Errors::IamAuthError,
|
|
82
|
+
'IamAuthPlugin: unable to determine connection region. ' \
|
|
83
|
+
"If you are using a non-standard RDS URL, please set the 'iam_region' property."
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
token_prop = PropertyDefinition::IAM_ACCESS_TOKEN_PROPERTY_NAME.get(wrapper_props_override).to_sym
|
|
87
|
+
|
|
88
|
+
port = Utils::IamAuthUtils.resolve_port(
|
|
89
|
+
PropertyDefinition::IAM_PORT.get(wrapper_props_override),
|
|
90
|
+
host_info,
|
|
91
|
+
@service_container.dialect_service.db_dialect.default_port
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
credentials = Utils::AwsCredentialsUtils.snapshot(@credentials_provider)
|
|
95
|
+
cache_key = token_cache_key(region, host, port, user, credentials)
|
|
96
|
+
entry = cache_key && @service_container.storage_service.get(IAM_TOKEN_CACHE_NAME, cache_key)
|
|
97
|
+
expiration = PropertyDefinition::IAM_EXPIRATION_SEC.get_float(wrapper_props_override)
|
|
98
|
+
PropertyDefinition::IAM_EXPIRATION_SEC.validate!(expiration)
|
|
99
|
+
|
|
100
|
+
if Utils::IamAuthUtils.valid_entry?(entry)
|
|
101
|
+
driver_props[token_prop] = entry.token
|
|
102
|
+
is_cached_token = true
|
|
103
|
+
else
|
|
104
|
+
driver_props[token_prop] = fetch_and_cache_token(region, host, port, user, credentials, cache_key, expiration)
|
|
105
|
+
is_cached_token = false
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
begin
|
|
109
|
+
pipeline_callable.call
|
|
110
|
+
rescue StandardError => e
|
|
111
|
+
raise unless is_cached_token && @service_container.dialect_service.login_error?(e)
|
|
112
|
+
|
|
113
|
+
driver_props[token_prop] = fetch_and_cache_token(region, host, port, user, credentials, cache_key, expiration)
|
|
114
|
+
pipeline_callable.call
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# The cache key for a token signed with the given credentials snapshot, or nil when there are
|
|
119
|
+
# no credentials. Without credentials there is nothing to tell one connection's token from
|
|
120
|
+
# another's, so such a connection neither reads nor writes the shared cache.
|
|
121
|
+
def token_cache_key(region, host, port, user, credentials)
|
|
122
|
+
return nil if credentials.nil?
|
|
123
|
+
|
|
124
|
+
Utils::IamAuthUtils.cache_key(region, host, port, user, Utils::AwsCredentialsUtils.identity(credentials))
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Generates a token signed with the same credentials snapshot its cache key was built from
|
|
128
|
+
# and, when there is a cache key, caches it. Without a snapshot the provider is passed as is,
|
|
129
|
+
# and signing fails for lack of credentials.
|
|
130
|
+
def fetch_and_cache_token(region, host, port, user, credentials, cache_key, expiration)
|
|
131
|
+
token = Aws::RDS::AuthTokenGenerator.new(credentials: credentials || @credentials_provider)
|
|
132
|
+
.auth_token(region:, endpoint: "#{host}:#{port}", user_name: user)
|
|
133
|
+
if cache_key
|
|
134
|
+
@service_container.storage_service.set(
|
|
135
|
+
IAM_TOKEN_CACHE_NAME, cache_key, Utils::IamAuthUtils.build_token_entry(token, expiration)
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
token
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def rds_client
|
|
142
|
+
@rds_client ||= begin
|
|
143
|
+
region = PropertyDefinition::IAM_REGION.get(@wrapper_props)
|
|
144
|
+
Aws::RDS::Client.new(
|
|
145
|
+
credentials: @credentials_provider,
|
|
146
|
+
**(region ? { region: region } : {})
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def ensure_aws_sdk!
|
|
152
|
+
require 'aws-sdk-rds'
|
|
153
|
+
rescue LoadError
|
|
154
|
+
raise LoadError,
|
|
155
|
+
"The IAM auth plugin requires 'aws-sdk-rds'. Add it to your Gemfile: gem 'aws-sdk-rds'"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|