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,266 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
|
7
|
+
# You may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
|
|
18
|
+
require_relative '../../monitoring/monitor'
|
|
19
|
+
require_relative '../../logging'
|
|
20
|
+
require_relative 'info'
|
|
21
|
+
require_relative 'member_list_type'
|
|
22
|
+
require 'concurrent'
|
|
23
|
+
|
|
24
|
+
module AwsAdvancedRubyDriverWrapper
|
|
25
|
+
module Plugins
|
|
26
|
+
module CustomEndpoint
|
|
27
|
+
class CustomEndpointMonitor < Monitoring::Monitor
|
|
28
|
+
include Logging
|
|
29
|
+
|
|
30
|
+
TERMINATION_TIMEOUT_SEC = 30.0
|
|
31
|
+
ENDPOINT_INFO_EXPIRATION_SEC = 300.0
|
|
32
|
+
UNAUTHORIZED_SLEEP_SEC = 300.0
|
|
33
|
+
ENDPOINT_INFO_CACHE_NAME = :custom_endpoint
|
|
34
|
+
ALLOWED_BLOCKED_CACHE_NAME = :custom_endpoint_allowed_blocked
|
|
35
|
+
|
|
36
|
+
def initialize(
|
|
37
|
+
service_container,
|
|
38
|
+
custom_endpoint_host,
|
|
39
|
+
endpoint_id,
|
|
40
|
+
region,
|
|
41
|
+
refresh_rate_sec,
|
|
42
|
+
refresh_rate_backoff_factor,
|
|
43
|
+
max_refresh_rate_sec,
|
|
44
|
+
rds_client_func: ->(_, region) { Aws::RDS::Client.new(region: region) }
|
|
45
|
+
)
|
|
46
|
+
super(termination_timeout_sec: TERMINATION_TIMEOUT_SEC)
|
|
47
|
+
|
|
48
|
+
@service_container = service_container
|
|
49
|
+
@custom_endpoint_host = custom_endpoint_host
|
|
50
|
+
@endpoint_id = endpoint_id
|
|
51
|
+
@min_refresh_rate_sec = refresh_rate_sec
|
|
52
|
+
@refresh_rate_sec = refresh_rate_sec
|
|
53
|
+
@refresh_rate_backoff_factor = refresh_rate_backoff_factor
|
|
54
|
+
@max_refresh_rate_sec = max_refresh_rate_sec
|
|
55
|
+
@rds_client = rds_client_func.call(custom_endpoint_host, region)
|
|
56
|
+
|
|
57
|
+
@refresh_mutex = Mutex.new
|
|
58
|
+
@refresh_cv = ConditionVariable.new
|
|
59
|
+
@refresh_required = false
|
|
60
|
+
@connection_issue = false
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def endpoint_info?
|
|
64
|
+
info = cached_info
|
|
65
|
+
request_refresh if info.nil? && @refresh_mutex.synchronize { !@refresh_required && !@connection_issue }
|
|
66
|
+
!info.nil?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Waits up to timeout_sec for the monitor to place endpoint info in the cache. Polls rather than
|
|
70
|
+
# waiting on a one-shot signal: #endpoint_info? re-checks the cache and re-requests a refresh
|
|
71
|
+
# (waking the monitor) on each pass, so a transient empty cache - e.g. an entry that aged out
|
|
72
|
+
# between monitor iterations - is repopulated within the window instead of failing immediately.
|
|
73
|
+
def wait_for_info?(timeout_sec)
|
|
74
|
+
deadline = monotonic_time + timeout_sec
|
|
75
|
+
loop do
|
|
76
|
+
return true if endpoint_info?
|
|
77
|
+
return false if monotonic_time >= deadline
|
|
78
|
+
|
|
79
|
+
sleep(0.1)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def request_endpoint_info_update
|
|
84
|
+
return if @refresh_mutex.synchronize { @connection_issue }
|
|
85
|
+
|
|
86
|
+
request_refresh
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def close
|
|
90
|
+
remove_cached_info
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.clear_cache(storage_service)
|
|
94
|
+
storage_service.clear(ENDPOINT_INFO_CACHE_NAME)
|
|
95
|
+
storage_service.clear(ALLOWED_BLOCKED_CACHE_NAME)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def monitor
|
|
101
|
+
logger.debug("[#{@endpoint_id}] Started custom endpoint monitor for #{@custom_endpoint_host.url}")
|
|
102
|
+
|
|
103
|
+
until stopped?
|
|
104
|
+
update_activity
|
|
105
|
+
run_monitor_iteration
|
|
106
|
+
end
|
|
107
|
+
rescue StandardError => e
|
|
108
|
+
logger.error("[#{@endpoint_id}] Unexpected error in custom endpoint monitor: #{e.message}")
|
|
109
|
+
ensure
|
|
110
|
+
remove_cached_info
|
|
111
|
+
logger.debug("[#{@endpoint_id}] Stopped custom endpoint monitor for #{@custom_endpoint_host.url}")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def run_monitor_iteration
|
|
115
|
+
start = monotonic_time
|
|
116
|
+
|
|
117
|
+
endpoints = fetch_endpoints
|
|
118
|
+
return unless endpoints
|
|
119
|
+
|
|
120
|
+
@refresh_mutex.synchronize do
|
|
121
|
+
@connection_issue = false
|
|
122
|
+
@refresh_required = false
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
unless valid_endpoint_count?(endpoints)
|
|
126
|
+
sleep_ignoring_refresh_requests(@refresh_rate_sec)
|
|
127
|
+
return
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
endpoint_info = Info.from_db_cluster_endpoint(endpoints.first)
|
|
131
|
+
process_endpoint_info(endpoint_info, start)
|
|
132
|
+
rescue Aws::RDS::Errors::ServiceError => e
|
|
133
|
+
handle_rds_error(e)
|
|
134
|
+
rescue StandardError => e
|
|
135
|
+
logger.error("[#{@endpoint_id}] Exception monitoring #{@custom_endpoint_host.url}: #{e.message}")
|
|
136
|
+
sleep_ignoring_refresh_requests(@refresh_rate_sec)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def fetch_endpoints
|
|
140
|
+
response = @rds_client.describe_db_cluster_endpoints(
|
|
141
|
+
db_cluster_endpoint_identifier: @endpoint_id,
|
|
142
|
+
filters: [{ name: 'db-cluster-endpoint-type', values: ['custom'] }]
|
|
143
|
+
)
|
|
144
|
+
response.db_cluster_endpoints
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def valid_endpoint_count?(endpoints)
|
|
148
|
+
return true if endpoints.size == 1
|
|
149
|
+
|
|
150
|
+
logger.warn("[#{@endpoint_id}] Expected 1 endpoint, got #{endpoints.size}: #{endpoints.map(&:endpoint)}")
|
|
151
|
+
false
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def process_endpoint_info(endpoint_info, start)
|
|
155
|
+
if cached_info == endpoint_info
|
|
156
|
+
elapsed = monotonic_time - start
|
|
157
|
+
interruptible_sleep([0, @refresh_rate_sec - elapsed].max)
|
|
158
|
+
return
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
logger.debug("[#{@endpoint_id}] Custom endpoint info changed: #{endpoint_info}")
|
|
162
|
+
cache_allowed_blocked(endpoint_info)
|
|
163
|
+
cache_info(endpoint_info)
|
|
164
|
+
speedup_refresh_rate
|
|
165
|
+
|
|
166
|
+
elapsed = monotonic_time - start
|
|
167
|
+
interruptible_sleep([0, @refresh_rate_sec - elapsed].max)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def cache_allowed_blocked(endpoint_info)
|
|
171
|
+
value = if endpoint_info.member_list_type == MemberListType::STATIC_LIST
|
|
172
|
+
{ allowed: endpoint_info.static_members, blocked: nil, required_role: endpoint_info.required_role }
|
|
173
|
+
else
|
|
174
|
+
{ allowed: nil, blocked: endpoint_info.excluded_members, required_role: endpoint_info.required_role }
|
|
175
|
+
end
|
|
176
|
+
storage_service.set(ALLOWED_BLOCKED_CACHE_NAME, @custom_endpoint_host.url, value)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def handle_rds_error(error)
|
|
180
|
+
logger.error("[#{@endpoint_id}] RDS error for #{@custom_endpoint_host.url}: #{error.message}")
|
|
181
|
+
|
|
182
|
+
if throttling_error?(error)
|
|
183
|
+
slowdown_refresh_rate
|
|
184
|
+
sleep_ignoring_refresh_requests(@refresh_rate_sec)
|
|
185
|
+
elsif unauthorized_error?(error)
|
|
186
|
+
sleep_ignoring_refresh_requests(UNAUTHORIZED_SLEEP_SEC)
|
|
187
|
+
else
|
|
188
|
+
sleep_ignoring_refresh_requests(@refresh_rate_sec)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def throttling_error?(error)
|
|
193
|
+
error.context&.http_response&.status_code == 429 ||
|
|
194
|
+
error.code == 'ThrottlingException' ||
|
|
195
|
+
error.code == 'Throttling'
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def unauthorized_error?(error)
|
|
199
|
+
[401, 403].include?(error.context&.http_response&.status_code)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def speedup_refresh_rate
|
|
203
|
+
return unless @refresh_rate_sec > @min_refresh_rate_sec
|
|
204
|
+
|
|
205
|
+
@refresh_rate_sec = [@refresh_rate_sec / @refresh_rate_backoff_factor, @min_refresh_rate_sec].max
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def slowdown_refresh_rate
|
|
209
|
+
return unless @refresh_rate_sec < @max_refresh_rate_sec
|
|
210
|
+
|
|
211
|
+
@refresh_rate_sec = [@refresh_rate_sec * @refresh_rate_backoff_factor, @max_refresh_rate_sec].min
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def request_refresh
|
|
215
|
+
@refresh_mutex.synchronize do
|
|
216
|
+
@refresh_required = true
|
|
217
|
+
@refresh_cv.broadcast
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Wakes early if a refresh is requested or the monitor is stopped.
|
|
222
|
+
def interruptible_sleep(duration_sec)
|
|
223
|
+
end_time = monotonic_time + duration_sec
|
|
224
|
+
wait_sec = [0.5, duration_sec].min
|
|
225
|
+
|
|
226
|
+
@refresh_mutex.synchronize do
|
|
227
|
+
@refresh_cv.wait(@refresh_mutex, wait_sec) until @refresh_required || monotonic_time >= end_time || stopped?
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Sleeps for the full duration, ignoring refresh requests. Used on error/backoff paths
|
|
232
|
+
# to prevent connections from bypassing throttling backoff.
|
|
233
|
+
def sleep_ignoring_refresh_requests(duration_sec)
|
|
234
|
+
end_time = monotonic_time + duration_sec
|
|
235
|
+
until stopped?
|
|
236
|
+
remaining = end_time - monotonic_time
|
|
237
|
+
break if remaining <= 0
|
|
238
|
+
|
|
239
|
+
sleep([0.5, remaining].min)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def cached_info
|
|
244
|
+
storage_service.get(ENDPOINT_INFO_CACHE_NAME, @custom_endpoint_host.url, register_access: false)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def cache_info(info)
|
|
248
|
+
storage_service.set(ENDPOINT_INFO_CACHE_NAME, @custom_endpoint_host.url, info)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def remove_cached_info
|
|
252
|
+
storage_service.remove(ENDPOINT_INFO_CACHE_NAME, @custom_endpoint_host.url)
|
|
253
|
+
storage_service.remove(ALLOWED_BLOCKED_CACHE_NAME, @custom_endpoint_host.url)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def storage_service
|
|
257
|
+
@service_container.storage_service
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def monotonic_time
|
|
261
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
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 '../../logging'
|
|
20
|
+
require_relative '../../property_definition'
|
|
21
|
+
require_relative '../../ruby_method'
|
|
22
|
+
require_relative '../../utils/rds_utils'
|
|
23
|
+
require_relative 'custom_endpoint_monitor'
|
|
24
|
+
|
|
25
|
+
module AwsAdvancedRubyDriverWrapper
|
|
26
|
+
module Plugins
|
|
27
|
+
module CustomEndpoint
|
|
28
|
+
class CustomEndpointPlugin
|
|
29
|
+
include Logging
|
|
30
|
+
|
|
31
|
+
MONITOR_TYPE = :custom_endpoint
|
|
32
|
+
|
|
33
|
+
attr_reader :subscribed_methods
|
|
34
|
+
|
|
35
|
+
def initialize(service_container, props = ::Concurrent::Map.new)
|
|
36
|
+
ensure_aws_sdk!
|
|
37
|
+
@service_container = service_container
|
|
38
|
+
@props = props
|
|
39
|
+
@should_wait_for_info = PropertyDefinition::WAIT_FOR_CUSTOM_ENDPOINT_INFO.get_bool(props)
|
|
40
|
+
@wait_timeout_sec = PropertyDefinition::WAIT_FOR_CUSTOM_ENDPOINT_INFO_TIMEOUT_SEC.get_float(props)
|
|
41
|
+
|
|
42
|
+
@custom_endpoint_host = nil
|
|
43
|
+
@endpoint_id = nil
|
|
44
|
+
@region = nil
|
|
45
|
+
|
|
46
|
+
monitor_expiration_sec = PropertyDefinition::CUSTOM_ENDPOINT_MONITOR_EXPIRATION_SEC.get_float(props)
|
|
47
|
+
service_container.monitor_service.register_type(
|
|
48
|
+
MONITOR_TYPE,
|
|
49
|
+
expiration_timeout_sec: monitor_expiration_sec,
|
|
50
|
+
produced_data_type: CustomEndpointMonitor::ENDPOINT_INFO_CACHE_NAME
|
|
51
|
+
)
|
|
52
|
+
service_container.storage_service.register(
|
|
53
|
+
CustomEndpointMonitor::ENDPOINT_INFO_CACHE_NAME,
|
|
54
|
+
ttl: CustomEndpointMonitor::ENDPOINT_INFO_EXPIRATION_SEC
|
|
55
|
+
)
|
|
56
|
+
service_container.storage_service.register(
|
|
57
|
+
CustomEndpointMonitor::ALLOWED_BLOCKED_CACHE_NAME,
|
|
58
|
+
ttl: CustomEndpointMonitor::ENDPOINT_INFO_EXPIRATION_SEC
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
network_methods = service_container.dialect_service.driver_dialect.network_bound_methods
|
|
62
|
+
@subscribed_methods = (Set['connect'] | network_methods).freeze
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def connect(host_info, _, _, pipeline_callable)
|
|
66
|
+
return pipeline_callable.call unless Utils::RdsUtils.rds_custom_cluster_dns?(host_info.host)
|
|
67
|
+
|
|
68
|
+
logger.debug("CustomEndpointPlugin: connection request to custom endpoint '#{host_info.url}'")
|
|
69
|
+
init_endpoint_state!(host_info) if @custom_endpoint_host.nil?
|
|
70
|
+
monitor = create_monitor_if_absent(@props)
|
|
71
|
+
wait_for_endpoint_info(monitor) if @should_wait_for_info
|
|
72
|
+
pipeline_callable.call
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
CLOSE_METHODS = Set[
|
|
76
|
+
RubyMethod::CONNECTION_CLOSE.name,
|
|
77
|
+
RubyMethod::CONNECTION_RESET.name
|
|
78
|
+
].freeze
|
|
79
|
+
|
|
80
|
+
def execute(method_name, pipeline_callable, *_, **_)
|
|
81
|
+
return pipeline_callable.call if @custom_endpoint_host.nil?
|
|
82
|
+
return pipeline_callable.call if CLOSE_METHODS.include?(method_name.to_s)
|
|
83
|
+
|
|
84
|
+
monitor = create_monitor_if_absent(@props)
|
|
85
|
+
wait_for_endpoint_info(monitor) if @should_wait_for_info
|
|
86
|
+
pipeline_callable.call
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.clear_cache(storage_service)
|
|
90
|
+
CustomEndpointMonitor.clear_cache(storage_service)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def init_endpoint_state!(host_info)
|
|
96
|
+
@custom_endpoint_host = host_info
|
|
97
|
+
|
|
98
|
+
endpoint_id = Utils::RdsUtils.rds_cluster_id(host_info.host)
|
|
99
|
+
if endpoint_id.nil? || endpoint_id.empty?
|
|
100
|
+
raise Errors::AwsError, "CustomEndpointPlugin: unable to parse endpoint identifier from '#{host_info.host}'"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
@endpoint_id = endpoint_id
|
|
104
|
+
@region = resolve_region!(host_info.host)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def resolve_region!(host)
|
|
108
|
+
region = PropertyDefinition::CUSTOM_ENDPOINT_REGION.get(@props) || Utils::RdsUtils.rds_region(host)
|
|
109
|
+
unless region
|
|
110
|
+
raise Errors::AwsError,
|
|
111
|
+
"CustomEndpointPlugin: unable to determine region for '#{host}'. " \
|
|
112
|
+
"Set the '#{PropertyDefinition::CUSTOM_ENDPOINT_REGION.name}' property explicitly."
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
region
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def create_monitor_if_absent(props)
|
|
119
|
+
@service_container.monitor_service.run_if_absent(
|
|
120
|
+
MONITOR_TYPE,
|
|
121
|
+
@custom_endpoint_host.url,
|
|
122
|
+
@service_container
|
|
123
|
+
) do |service_container|
|
|
124
|
+
CustomEndpointMonitor.new(
|
|
125
|
+
service_container,
|
|
126
|
+
@custom_endpoint_host,
|
|
127
|
+
@endpoint_id,
|
|
128
|
+
@region,
|
|
129
|
+
PropertyDefinition::CUSTOM_ENDPOINT_INFO_REFRESH_RATE_SEC.get_float(props),
|
|
130
|
+
PropertyDefinition::CUSTOM_ENDPOINT_INFO_REFRESH_RATE_BACKOFF_FACTOR.get_int(props),
|
|
131
|
+
PropertyDefinition::CUSTOM_ENDPOINT_INFO_MAX_REFRESH_RATE_SEC.get_float(props)
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def wait_for_endpoint_info(monitor)
|
|
137
|
+
return if monitor.endpoint_info?
|
|
138
|
+
|
|
139
|
+
monitor.request_endpoint_info_update
|
|
140
|
+
logger.debug("CustomEndpointPlugin: waiting up to #{@wait_timeout_sec}s for endpoint info on #{@custom_endpoint_host.url}")
|
|
141
|
+
|
|
142
|
+
return if monitor.wait_for_info?(@wait_timeout_sec)
|
|
143
|
+
|
|
144
|
+
raise Errors::AwsError,
|
|
145
|
+
"CustomEndpointPlugin: timed out after #{@wait_timeout_sec}s " \
|
|
146
|
+
"waiting for custom endpoint info for '#{@custom_endpoint_host.url}'"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def ensure_aws_sdk!
|
|
150
|
+
require 'aws-sdk-rds'
|
|
151
|
+
rescue LoadError
|
|
152
|
+
raise LoadError,
|
|
153
|
+
"The custom endpoint plugin requires 'aws-sdk-rds'. Add it to your Gemfile: gem 'aws-sdk-rds'"
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
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 'member_list_type'
|
|
18
|
+
require_relative 'role'
|
|
19
|
+
|
|
20
|
+
module AwsAdvancedRubyDriverWrapper
|
|
21
|
+
module Plugins
|
|
22
|
+
module CustomEndpoint
|
|
23
|
+
# Represents custom endpoint information for a given custom endpoint.
|
|
24
|
+
class Info
|
|
25
|
+
attr_reader :endpoint_identifier, :cluster_identifier, :url, :role, :member_list_type, :members
|
|
26
|
+
|
|
27
|
+
# @param endpoint_identifier [String] The endpoint identifier for the custom endpoint. For example, if the
|
|
28
|
+
# custom endpoint URL is "my-custom-endpoint.cluster-custom-XYZ.us-east-1.rds.amazonaws.com", the endpoint
|
|
29
|
+
# identifier is "my-custom-endpoint".
|
|
30
|
+
# @param cluster_identifier [String] The cluster identifier for the cluster that the custom endpoint belongs to.
|
|
31
|
+
# @param url [String] The URL for the custom endpoint.
|
|
32
|
+
# @param role [Symbol] The role of the custom endpoint.
|
|
33
|
+
# @param members [Enumerable<String>] The instance IDs for the hosts in the custom endpoint.
|
|
34
|
+
# @param member_list_type [Symbol] The list type for +members+.
|
|
35
|
+
def initialize(endpoint_identifier:,
|
|
36
|
+
cluster_identifier:,
|
|
37
|
+
url:,
|
|
38
|
+
role:,
|
|
39
|
+
members:,
|
|
40
|
+
member_list_type:)
|
|
41
|
+
@endpoint_identifier = endpoint_identifier
|
|
42
|
+
@cluster_identifier = cluster_identifier
|
|
43
|
+
@url = url
|
|
44
|
+
@role = role
|
|
45
|
+
@members = members.to_set.freeze
|
|
46
|
+
@member_list_type = member_list_type
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.from_db_cluster_endpoint(response)
|
|
50
|
+
static = response.static_members&.any?
|
|
51
|
+
members = static ? response.static_members : response.excluded_members
|
|
52
|
+
member_list_type = static ? MemberListType::STATIC_LIST : MemberListType::EXCLUSION_LIST
|
|
53
|
+
|
|
54
|
+
new(
|
|
55
|
+
endpoint_identifier: response.db_cluster_endpoint_identifier,
|
|
56
|
+
cluster_identifier: response.db_cluster_identifier,
|
|
57
|
+
url: response.endpoint,
|
|
58
|
+
role: Role.parse(response.custom_endpoint_type),
|
|
59
|
+
members: members,
|
|
60
|
+
member_list_type: member_list_type
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Evaluates whether instances in the custom endpoint must match a particular role according to the custom
|
|
65
|
+
# endpoint properties. Note that custom clusters with static member lists always route to all static members,
|
|
66
|
+
# even if the member is a writer and the custom endpoint is of type READER, so there are never role
|
|
67
|
+
# requirements for static list custom clusters.
|
|
68
|
+
# @return [Symbol, nil] the required role of instances in the custom endpoint, or nil if there is no strict
|
|
69
|
+
# role requirement.
|
|
70
|
+
def required_role
|
|
71
|
+
:reader if @member_list_type == MemberListType::EXCLUSION_LIST && @role == Role::READER
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Gets the static members of the custom endpoint. If the custom endpoint member list type is an exclusion
|
|
75
|
+
# list, returns nil.
|
|
76
|
+
# @return [Set<String>, nil]
|
|
77
|
+
def static_members
|
|
78
|
+
@members if @member_list_type == MemberListType::STATIC_LIST
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Gets the excluded members of the custom endpoint. If the custom endpoint member list type is a static
|
|
82
|
+
# list, returns nil.
|
|
83
|
+
# @return [Set<String>, nil]
|
|
84
|
+
def excluded_members
|
|
85
|
+
@members if @member_list_type == MemberListType::EXCLUSION_LIST
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def ==(other)
|
|
89
|
+
other.is_a?(Info) &&
|
|
90
|
+
endpoint_identifier == other.endpoint_identifier &&
|
|
91
|
+
cluster_identifier == other.cluster_identifier &&
|
|
92
|
+
url == other.url &&
|
|
93
|
+
role == other.role &&
|
|
94
|
+
member_list_type == other.member_list_type &&
|
|
95
|
+
members == other.members
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
alias eql? ==
|
|
99
|
+
|
|
100
|
+
def hash
|
|
101
|
+
[@endpoint_identifier, @cluster_identifier, @url, @role, @member_list_type, @members].hash
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def to_s
|
|
105
|
+
"Info[url=#{@url}, cluster=#{@cluster_identifier}, role=#{@role}, " \
|
|
106
|
+
"member_list_type=#{@member_list_type}, members=#{@members}]"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
module AwsAdvancedRubyDriverWrapper
|
|
18
|
+
module Plugins
|
|
19
|
+
module CustomEndpoint
|
|
20
|
+
# Represents the member list type of a custom endpoint.
|
|
21
|
+
# Used with a member list to determine which instances are included or excluded.
|
|
22
|
+
module MemberListType
|
|
23
|
+
# Only the listed instances are included. New cluster instances are NOT auto-added.
|
|
24
|
+
STATIC_LIST = :static_list
|
|
25
|
+
|
|
26
|
+
# The listed instances are excluded. New cluster instances ARE auto-added.
|
|
27
|
+
EXCLUSION_LIST = :exclusion_list
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
module AwsAdvancedRubyDriverWrapper
|
|
18
|
+
module Plugins
|
|
19
|
+
module CustomEndpoint
|
|
20
|
+
# Represents the possible roles of instances specified by a custom endpoint.
|
|
21
|
+
module Role
|
|
22
|
+
# Instances may be either a writer or a reader.
|
|
23
|
+
ANY = :any
|
|
24
|
+
|
|
25
|
+
# Instance is always the writer.
|
|
26
|
+
WRITER = :writer
|
|
27
|
+
|
|
28
|
+
# Instances are always readers.
|
|
29
|
+
READER = :reader
|
|
30
|
+
|
|
31
|
+
ROLE_MAPPING = {
|
|
32
|
+
'ANY' => ANY,
|
|
33
|
+
'WRITER' => WRITER,
|
|
34
|
+
'READER' => READER
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
def self.parse(value)
|
|
38
|
+
raise ArgumentError, 'Role value is blank' if value.nil? || value.strip.empty?
|
|
39
|
+
|
|
40
|
+
ROLE_MAPPING.fetch(value.upcase) { raise ArgumentError, "Unknown role: #{value}" }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|