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.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +175 -0
  4. data/NOTICE +1 -0
  5. data/README.md +168 -0
  6. data/THIRD-PARTY-LICENSES +473 -0
  7. data/aws_advanced_ruby_driver_wrapper.gemspec +73 -0
  8. data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_mysql2_adapter.rb +73 -0
  9. data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_postgresql_adapter.rb +95 -0
  10. data/lib/aws_advanced_ruby_driver_wrapper/custom_configuration.rb +58 -0
  11. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_mysql_dialect.rb +103 -0
  12. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_pg_dialect.rb +124 -0
  13. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/dialect_codes.rb +38 -0
  14. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_mysql_dialect.rb +91 -0
  15. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_pg_dialect.rb +92 -0
  16. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_mysql_dialect.rb +95 -0
  17. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_pg_dialect.rb +86 -0
  18. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/mysql_dialect.rb +98 -0
  19. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/pg_dialect.rb +95 -0
  20. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_mysql_dialect.rb +88 -0
  21. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_pg_dialect.rb +86 -0
  22. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/unknown_dialect.rb +72 -0
  23. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/utils/dialect_utils.rb +71 -0
  24. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect.rb +154 -0
  25. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect_manager.rb +55 -0
  26. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/mysql_driver_dialect.rb +165 -0
  27. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/pg_driver_dialect.rb +201 -0
  28. data/lib/aws_advanced_ruby_driver_wrapper/errors/error_handler.rb +62 -0
  29. data/lib/aws_advanced_ruby_driver_wrapper/errors/mysql_error_handler.rb +80 -0
  30. data/lib/aws_advanced_ruby_driver_wrapper/errors/pg_error_handler.rb +126 -0
  31. data/lib/aws_advanced_ruby_driver_wrapper/errors.rb +59 -0
  32. data/lib/aws_advanced_ruby_driver_wrapper/host/connection_string_host_list_provider.rb +95 -0
  33. data/lib/aws_advanced_ruby_driver_wrapper/host/global_aurora_host_list_provider.rb +65 -0
  34. data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability.rb +24 -0
  35. data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability_strategy.rb +27 -0
  36. data/lib/aws_advanced_ruby_driver_wrapper/host/host_info.rb +137 -0
  37. data/lib/aws_advanced_ruby_driver_wrapper/host/host_role.rb +25 -0
  38. data/lib/aws_advanced_ruby_driver_wrapper/host/random_host_selector.rb +40 -0
  39. data/lib/aws_advanced_ruby_driver_wrapper/host/rds_host_list_provider.rb +206 -0
  40. data/lib/aws_advanced_ruby_driver_wrapper/logging.rb +110 -0
  41. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/cluster_topology_monitor.rb +709 -0
  42. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/global_cluster_topology_monitor.rb +72 -0
  43. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor.rb +99 -0
  44. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_connection.rb +57 -0
  45. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_state.rb +25 -0
  46. data/lib/aws_advanced_ruby_driver_wrapper/mysql.rb +429 -0
  47. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/blue_green_plugin.rb +205 -0
  48. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/host_mapper.rb +132 -0
  49. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/iam_host_tracker.rb +84 -0
  50. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interim_status.rb +92 -0
  51. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interval_rate.rb +27 -0
  52. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase.rb +69 -0
  53. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_event_log.rb +85 -0
  54. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_time_info.rb +25 -0
  55. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/role.rb +38 -0
  56. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/base_routing.rb +83 -0
  57. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/reject_connect_routing.rb +40 -0
  58. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/substitute_connect_routing.rb +136 -0
  59. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_connect_routing.rb +53 -0
  60. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_execute_routing.rb +52 -0
  61. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_until_corresponding_host_found_connect_routing.rb +83 -0
  62. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status.rb +68 -0
  63. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_builder.rb +244 -0
  64. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_info.rb +30 -0
  65. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_monitor.rb +564 -0
  66. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_provider.rb +414 -0
  67. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_state.rb +98 -0
  68. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_timer.rb +46 -0
  69. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_monitor.rb +266 -0
  70. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_plugin.rb +158 -0
  71. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/info.rb +111 -0
  72. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/member_list_type.rb +31 -0
  73. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/role.rb +45 -0
  74. data/lib/aws_advanced_ruby_driver_wrapper/plugins/default_plugin.rb +108 -0
  75. data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_mode.rb +43 -0
  76. data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_plugin.rb +467 -0
  77. data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_mode.rb +68 -0
  78. data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_plugin.rb +403 -0
  79. data/lib/aws_advanced_ruby_driver_wrapper/plugins/iam_auth_plugin.rb +159 -0
  80. data/lib/aws_advanced_ruby_driver_wrapper/plugins/initial_connection_strategy_plugin.rb +485 -0
  81. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/audit_logger.rb +157 -0
  82. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_cipher.rb +159 -0
  83. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_encryption_config.rb +61 -0
  84. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/connection_source.rb +91 -0
  85. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/data_key_cache.rb +220 -0
  86. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_algorithm.rb +75 -0
  87. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_config.rb +146 -0
  88. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_service.rb +391 -0
  89. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/error_context.rb +198 -0
  90. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/errors.rb +259 -0
  91. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_management_utility.rb +435 -0
  92. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_manager.rb +378 -0
  93. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_metadata.rb +86 -0
  94. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_plugin.rb +890 -0
  95. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_utility.rb +281 -0
  96. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/metadata_manager.rb +332 -0
  97. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sanitizer.rb +147 -0
  98. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_name.rb +70 -0
  99. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_validator.rb +211 -0
  100. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sql_runner.rb +147 -0
  101. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/type_marker.rb +109 -0
  102. data/lib/aws_advanced_ruby_driver_wrapper/plugins/secrets_manager_plugin.rb +358 -0
  103. data/lib/aws_advanced_ruby_driver_wrapper/postgresql.rb +659 -0
  104. data/lib/aws_advanced_ruby_driver_wrapper/property_definition.rb +409 -0
  105. data/lib/aws_advanced_ruby_driver_wrapper/ruby_method.rb +122 -0
  106. data/lib/aws_advanced_ruby_driver_wrapper/services/connection_service.rb +143 -0
  107. data/lib/aws_advanced_ruby_driver_wrapper/services/dialect_service.rb +267 -0
  108. data/lib/aws_advanced_ruby_driver_wrapper/services/host_service.rb +199 -0
  109. data/lib/aws_advanced_ruby_driver_wrapper/services/monitor_service.rb +186 -0
  110. data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_call_context.rb +63 -0
  111. data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_manager.rb +273 -0
  112. data/lib/aws_advanced_ruby_driver_wrapper/services/service_container.rb +30 -0
  113. data/lib/aws_advanced_ruby_driver_wrapper/services/service_utility.rb +78 -0
  114. data/lib/aws_advanced_ruby_driver_wrapper/services/session_state_service.rb +56 -0
  115. data/lib/aws_advanced_ruby_driver_wrapper/utils/accessible_regions.rb +52 -0
  116. data/lib/aws_advanced_ruby_driver_wrapper/utils/ar_constants.rb +25 -0
  117. data/lib/aws_advanced_ruby_driver_wrapper/utils/aurora_topology_utils.rb +99 -0
  118. data/lib/aws_advanced_ruby_driver_wrapper/utils/aws_credentials_utils.rb +62 -0
  119. data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config.rb +91 -0
  120. data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config_parser.rb +368 -0
  121. data/lib/aws_advanced_ruby_driver_wrapper/utils/conversion_utils.rb +51 -0
  122. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/batching_event_publisher.rb +119 -0
  123. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/data_access_event.rb +26 -0
  124. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/monitor_reset_event.rb +26 -0
  125. data/lib/aws_advanced_ruby_driver_wrapper/utils/global_aurora_topology_utils.rb +185 -0
  126. data/lib/aws_advanced_ruby_driver_wrapper/utils/host_list_utils.rb +27 -0
  127. data/lib/aws_advanced_ruby_driver_wrapper/utils/iam_auth_utils.rb +112 -0
  128. data/lib/aws_advanced_ruby_driver_wrapper/utils/multi_az_topology_utils.rb +117 -0
  129. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/encryption_annotation_parser.rb +99 -0
  130. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/mysql_statement_analyzer.rb +641 -0
  131. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/pg_statement_analyzer.rb +502 -0
  132. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_analysis.rb +63 -0
  133. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_type.rb +35 -0
  134. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint.rb +27 -0
  135. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint_parser.rb +50 -0
  136. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/sql_parser.rb +139 -0
  137. data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_url_type.rb +71 -0
  138. data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_utils.rb +575 -0
  139. data/lib/aws_advanced_ruby_driver_wrapper/utils/retry_util.rb +153 -0
  140. data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_encoding.rb +56 -0
  141. data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_method_analyzer.rb +195 -0
  142. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/cache_entry.rb +56 -0
  143. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/expiration_cache.rb +108 -0
  144. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/sliding_expiration_cache.rb +137 -0
  145. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/storage_service.rb +172 -0
  146. data/lib/aws_advanced_ruby_driver_wrapper/utils/topology_utils.rb +127 -0
  147. data/lib/aws_advanced_ruby_driver_wrapper/version.rb +19 -0
  148. data/lib/aws_advanced_ruby_driver_wrapper/wrapper_property.rb +64 -0
  149. data/lib/aws_advanced_ruby_driver_wrapper.rb +116 -0
  150. metadata +227 -0
@@ -0,0 +1,414 @@
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 '../../logging'
19
+ require_relative '../../property_definition'
20
+ require_relative '../../utils/rds_utils'
21
+ require_relative 'iam_host_tracker'
22
+ require_relative 'interval_rate'
23
+ require_relative 'host_mapper'
24
+ require_relative 'phase'
25
+ require_relative 'phase_event_log'
26
+ require_relative 'phase_time_info'
27
+ require_relative 'role'
28
+ require_relative 'status'
29
+ require_relative 'status_builder'
30
+ require_relative 'status_monitor'
31
+ require_relative 'switchover_state'
32
+ require_relative 'switchover_timer'
33
+ require_relative '../../plugins/iam_auth_plugin'
34
+
35
+ module AwsAdvancedRubyDriverWrapper
36
+ module Plugins
37
+ module BlueGreen
38
+ class StatusProvider
39
+ include Logging
40
+
41
+ DEFAULT_CONNECT_TIMEOUT_SEC = 10
42
+ DEFAULT_SOCKET_TIMEOUT_SEC = 10
43
+
44
+ def initialize(service_container, wrapper_props, bgd_id, cluster_id)
45
+ @service_container = service_container
46
+ @storage_service = service_container.storage_service
47
+ @wrapper_props = wrapper_props
48
+ @bgd_id = bgd_id
49
+ @cluster_id = cluster_id
50
+
51
+ @monitoring_wrapper_props = build_monitoring_wrapper_props
52
+ @monitoring_driver_props = build_monitoring_driver_props
53
+
54
+ @monitors = { Role::SOURCE => nil, Role::TARGET => nil }
55
+ @interim_statuses = { Role::SOURCE => nil, Role::TARGET => nil }
56
+ @interim_status_hashes = { Role::SOURCE => 0, Role::TARGET => 0 }
57
+ @last_context_hash = 0
58
+ @summary_status = nil
59
+ @latest_status_phase = Phase::NOT_CREATED
60
+ @rollback = false
61
+ @process_status_lock = Mutex.new
62
+ @monitor_generation = 0
63
+
64
+ @status_check_interval_map = {
65
+ IntervalRate::BASELINE => PropertyDefinition::BG_INTERVAL_BASELINE_SEC.get_float(wrapper_props) * 1000,
66
+ IntervalRate::INCREASED => PropertyDefinition::BG_INTERVAL_INCREASED_SEC.get_float(wrapper_props) * 1000,
67
+ IntervalRate::HIGH => PropertyDefinition::BG_INTERVAL_HIGH_SEC.get_float(wrapper_props) * 1000
68
+ }
69
+
70
+ @green_topology_recognized_logged = false
71
+
72
+ @timer = SwitchoverTimer.new(PropertyDefinition::BG_SWITCHOVER_TIMEOUT_SEC.get_float(wrapper_props) * 1_000_000_000)
73
+ @event_log = PhaseEventLog.new
74
+ @host_mapper = HostMapper.new
75
+ @iam_tracker = IamHostTracker.new(on_all_changed: method(:on_all_green_hosts_changed))
76
+ @switchover_state = SwitchoverState.new(
77
+ on_monitor_reset: method(:publish_monitor_reset),
78
+ on_event: method(:record_event)
79
+ )
80
+ @builder = StatusBuilder.new(@bgd_id, @host_mapper, @timer, @interim_statuses, @iam_tracker, @switchover_state)
81
+
82
+ dialect = service_container.dialect_service.db_dialect
83
+ if dialect.respond_to?(:blue_green_status_available?)
84
+ init_monitoring
85
+ else
86
+ logger.warn do
87
+ "[bgd_id: '#{@bgd_id}'] Blue/Green Deployments isn't supported by database dialect #{dialect.class.name.split('::').last}."
88
+ end
89
+ end
90
+ end
91
+
92
+ def stop
93
+ @monitors.each_value { |m| m&.stop }
94
+ end
95
+
96
+ def log_switchover_final_summary
97
+ return unless switchover_finalized?
98
+
99
+ logger.info(@event_log.summary(@bgd_id, @rollback))
100
+ end
101
+
102
+ def log_current_context
103
+ phase_str = @summary_status&.current_phase&.to_s || '<null>'
104
+ logger.debug { "[bgd_id: '#{@bgd_id}'] BG status: #{phase_str}" }
105
+ logger.debug { "[bgd_id: '#{@bgd_id}'] Summary status:\n#{@summary_status || '<null>'}" }
106
+ logger.debug { "Corresponding hosts:\n#{@host_mapper.to_debug_s}" }
107
+ logger.debug { "Phase times:\n#{@event_log.to_debug_s}" }
108
+ logger.debug { "Green host certificate change times:\n#{@iam_tracker.to_debug_s}" }
109
+ logger.debug { "\n latest_status_phase: #{@latest_status_phase}\n#{@switchover_state.to_debug_s(@iam_tracker)}" }
110
+ end
111
+
112
+ def log_green_topology_recognized
113
+ return if @green_topology_recognized_logged
114
+ return unless green_topology_ready?
115
+
116
+ @green_topology_recognized_logged = true
117
+
118
+ source_status = @interim_statuses[Role::SOURCE]
119
+ target_status = @interim_statuses[Role::TARGET]
120
+ source_host_count = source_status.host_names.size
121
+ target_host_count = target_status.host_names.size
122
+ corresponding_host_count = @host_mapper.corresponding_hosts.count { |_, v| v[1] }
123
+
124
+ mapping_str = format_host_mapping
125
+ ip_str = format_ip_mapping
126
+
127
+ logger.info do
128
+ "[bgd_id: '#{@bgd_id}'] Blue/Green target topology recognized\n " \
129
+ "phase: #{@latest_status_phase}\n " \
130
+ "source_hosts: #{source_host_count}\n " \
131
+ "target_hosts: #{target_host_count}\n " \
132
+ "corresponding_hosts: #{corresponding_host_count}\n " \
133
+ "ready: true\n " \
134
+ "Blue -> Green Mapping:\n " \
135
+ "#{mapping_str}\n " \
136
+ "Host -> IP Mapping:\n " \
137
+ "#{ip_str}"
138
+ end
139
+ end
140
+
141
+ def green_topology_ready?
142
+ source_status = @interim_statuses[Role::SOURCE]
143
+ target_status = @interim_statuses[Role::TARGET]
144
+
145
+ source_status &&
146
+ target_status &&
147
+ source_status.start_topology&.any? &&
148
+ target_status.start_topology&.any? &&
149
+ source_status.host_names&.any? &&
150
+ target_status.host_names&.any? &&
151
+ @host_mapper.corresponding_hosts.any?
152
+ end
153
+
154
+ def format_host_mapping
155
+ rows = @host_mapper.corresponding_hosts.sort_by { |k, _| k }.map do |blue, pair|
156
+ "#{blue} -> #{pair[1]&.host_and_port || '<null>'}"
157
+ end
158
+ rows.any? ? rows.join("\n ") : '-'
159
+ end
160
+
161
+ def format_ip_mapping
162
+ rows = @host_mapper.host_ip_addresses.sort_by { |k, _| k }.map do |host, ip|
163
+ "#{host} -> #{ip || '<null>'}"
164
+ end
165
+ rows.any? ? rows.join("\n ") : '-'
166
+ end
167
+
168
+ def check_switchover_timer_expiry
169
+ return unless @timer.expired?
170
+ return unless [Phase::IN_PROGRESS, Phase::POST, Phase::PREPARATION].include?(@latest_status_phase)
171
+
172
+ logger.warn { 'Blue/Green switchover has timed out.' }
173
+ @summary_status = @rollback ? @builder.created : @builder.completed(@rollback)
174
+ update_monitors
175
+ update_status_cache
176
+ log_current_context
177
+ end
178
+
179
+ def reset_context_when_completed
180
+ return unless switchover_finalized?
181
+
182
+ logger.debug { 'Resetting context.' }
183
+
184
+ old_monitors = @monitors
185
+ @monitor_generation += 1
186
+ @monitors = { Role::SOURCE => nil, Role::TARGET => nil }
187
+ old_monitors.each_value { |m| m&.stop }
188
+
189
+ Plugins::IamAuthPlugin.clear_cache(@storage_service) if iam_enabled?
190
+
191
+ @rollback = false
192
+ @green_topology_recognized_logged = false
193
+ @summary_status = nil
194
+ @latest_status_phase = Phase::NOT_CREATED
195
+ @interim_status_hashes = { Role::SOURCE => 0, Role::TARGET => 0 }
196
+ @last_context_hash = 0
197
+ @interim_statuses = { Role::SOURCE => nil, Role::TARGET => nil }
198
+
199
+ @timer.reset
200
+ @event_log.clear
201
+ @host_mapper.clear
202
+ @iam_tracker.clear
203
+ @switchover_state.reset
204
+ @builder = StatusBuilder.new(@bgd_id, @host_mapper, @timer, @interim_statuses, @iam_tracker, @switchover_state)
205
+
206
+ init_monitoring
207
+ end
208
+
209
+ def update_summary_status(role, interim_status)
210
+ case @latest_status_phase
211
+ when Phase::NOT_CREATED then @summary_status = Status.new(@bgd_id, Phase::NOT_CREATED)
212
+ when Phase::CREATED
213
+ @switchover_state.update_dns_flags(@bgd_id, role, interim_status)
214
+ @summary_status = @builder.created
215
+ when Phase::PREPARATION
216
+ @timer.start
217
+ @switchover_state.update_dns_flags(@bgd_id, role, interim_status)
218
+ @summary_status = @builder.preparation(@rollback)
219
+ when Phase::IN_PROGRESS
220
+ @switchover_state.update_dns_flags(@bgd_id, role, interim_status)
221
+ @summary_status = @builder.in_progress(@rollback)
222
+ @switchover_state.trigger_in_progress_monitor_reset
223
+ when Phase::POST
224
+ @switchover_state.update_dns_flags(@bgd_id, role, interim_status)
225
+ @summary_status = @builder.post(@rollback)
226
+ when Phase::COMPLETED
227
+ @switchover_state.update_dns_flags(@bgd_id, role, interim_status)
228
+ @summary_status = @builder.completed(@rollback)
229
+ else
230
+ raise ArgumentError, "[bgd_id: '#{@bgd_id}'] Unknown BG phase '#{@latest_status_phase}'."
231
+ end
232
+ end
233
+
234
+ MONITOR_SETTINGS = {
235
+ Phase::NOT_CREATED => { interval: IntervalRate::BASELINE, collect_ips: false, collect_topo: false, use_ip: false }.freeze,
236
+ Phase::CREATED => { interval: IntervalRate::INCREASED, collect_ips: true, collect_topo: true, use_ip: false }.freeze,
237
+ Phase::PREPARATION => { interval: IntervalRate::HIGH, collect_ips: false, collect_topo: false, use_ip: true }.freeze,
238
+ Phase::IN_PROGRESS => { interval: IntervalRate::HIGH, collect_ips: false, collect_topo: false, use_ip: true }.freeze,
239
+ Phase::POST => { interval: IntervalRate::HIGH, collect_ips: false, collect_topo: false, use_ip: true }.freeze,
240
+ Phase::COMPLETED => { interval: IntervalRate::BASELINE, collect_ips: false, collect_topo: false, use_ip: false }.freeze
241
+ }.freeze
242
+
243
+ def update_monitors
244
+ settings = MONITOR_SETTINGS.fetch(@summary_status.current_phase) do
245
+ raise ArgumentError, "[bgd_id: '#{@bgd_id}'] Unknown BG phase '#{@summary_status.current_phase}'."
246
+ end
247
+
248
+ @monitors.values.compact.each do |monitor|
249
+ monitor.interval_rate = settings[:interval]
250
+ monitor.collect_ip_addresses = settings[:collect_ips]
251
+ monitor.collect_topology = settings[:collect_topo]
252
+ monitor.use_ip_address = settings[:use_ip]
253
+ monitor.reset_collected_data if (@rollback && @summary_status.current_phase == Phase::CREATED) ||
254
+ @summary_status.current_phase == Phase::COMPLETED
255
+ end
256
+ end
257
+
258
+ def update_status_cache
259
+ latest_status = @storage_service.get(BlueGreenPlugin::BLUE_GREEN_NAME, @bgd_id)
260
+ @storage_service.set(BlueGreenPlugin::BLUE_GREEN_NAME, @bgd_id, @summary_status)
261
+ store_phase_time(@summary_status.current_phase)
262
+ latest_status&.notify
263
+ end
264
+
265
+ def update_phase(role, interim_status)
266
+ new_phase = interim_status.blue_green_phase
267
+ latest_interim_phase = @interim_statuses[role]&.blue_green_phase || Phase::NOT_CREATED
268
+
269
+ if role == Role::TARGET &&
270
+ !new_phase.nil? &&
271
+ @latest_status_phase != Phase::COMPLETED &&
272
+ new_phase < latest_interim_phase
273
+ @rollback = true
274
+ logger.debug { "[bgd_id: '#{@bgd_id}'] Blue/Green deployment is in rollback mode." }
275
+ end
276
+
277
+ return if new_phase.nil?
278
+
279
+ if @rollback
280
+ @latest_status_phase = new_phase if new_phase < @latest_status_phase
281
+ elsif new_phase > @latest_status_phase
282
+ @latest_status_phase = new_phase
283
+ end
284
+ end
285
+
286
+ def prepare_status(role, interim_status, generation = @monitor_generation)
287
+ @process_status_lock.synchronize do
288
+ return if generation != @monitor_generation
289
+
290
+ status_hash = interim_status.hash
291
+ ctx_hash = context_hash
292
+
293
+ if @interim_status_hashes[role] == status_hash && @last_context_hash == ctx_hash
294
+ check_switchover_timer_expiry
295
+ return
296
+ end
297
+
298
+ logger.debug { "[bgd_id: '#{@bgd_id}', role: #{role}] #{interim_status}" }
299
+
300
+ update_phase(role, interim_status)
301
+
302
+ @interim_statuses[role] = interim_status
303
+ @interim_status_hashes[role] = status_hash
304
+ @last_context_hash = ctx_hash
305
+
306
+ @host_mapper.merge_ips(interim_status.start_ip_addresses_by_host)
307
+ @host_mapper.register_role(interim_status.host_names, role)
308
+ @host_mapper.update(@interim_statuses[Role::SOURCE], @interim_statuses[Role::TARGET])
309
+
310
+ update_summary_status(role, interim_status)
311
+ check_switchover_timer_expiry
312
+ update_monitors
313
+ update_status_cache
314
+ log_current_context
315
+ log_green_topology_recognized
316
+ log_switchover_final_summary
317
+ reset_context_when_completed
318
+ end
319
+ end
320
+
321
+ private
322
+
323
+ def record_event(label, phase: nil)
324
+ @event_log.record(label, @rollback ? ' (rollback)' : '', phase: phase)
325
+ end
326
+
327
+ def store_phase_time(phase)
328
+ return if phase.nil?
329
+
330
+ record_event(phase.to_s, phase: phase)
331
+ end
332
+
333
+ def on_all_green_hosts_changed
334
+ record_event('Green host certificates changed')
335
+ end
336
+
337
+ def publish_monitor_reset(_event_name)
338
+ blue_endpoints = @summary_status.role_by_host
339
+ .select { |_host, role| role == Role::SOURCE }
340
+ .keys
341
+ .to_set
342
+
343
+ @service_container.event_publisher.publish(
344
+ Utils::Events::MonitorResetEvent.new(cluster_id: @cluster_id, endpoints: blue_endpoints)
345
+ )
346
+ end
347
+
348
+ def switchover_finalized?
349
+ completed = (!@rollback && @summary_status.current_phase == Phase::COMPLETED) ||
350
+ (@rollback && @summary_status.current_phase == Phase::CREATED)
351
+ completed && @event_log.any? { |_, v| v.phase&.active_switchover_or_completed? }
352
+ end
353
+
354
+ def iam_enabled?
355
+ @service_container.plugin_manager&.plugin_in_use?(Plugins::IamAuthPlugin) || false
356
+ end
357
+
358
+ def context_hash
359
+ [@iam_tracker.all_changed?, @iam_tracker.size].hash
360
+ end
361
+
362
+ def init_monitoring
363
+ generation = @monitor_generation
364
+ [Role::SOURCE, Role::TARGET].each do |role|
365
+ monitor = StatusMonitor.new(
366
+ role,
367
+ @bgd_id,
368
+ @service_container.connection_service.current_host_info,
369
+ @service_container,
370
+ monitoring_driver_props: @monitoring_driver_props,
371
+ status_check_interval_map: @status_check_interval_map,
372
+ on_change_func: ->(r, s) { prepare_status(r, s, generation) },
373
+ monitoring_wrapper_props: @monitoring_wrapper_props
374
+ )
375
+ @monitors[role] = monitor
376
+ monitor.start
377
+ end
378
+ end
379
+
380
+ # Wrapper overrides are merged onto base wrapper_props with defaults applied.
381
+ # @return Hash monitoring_wrapper_props
382
+ def build_monitoring_wrapper_props
383
+ prefixed_wrapper_config = @service_container.connection_service.prefixed_wrapper_config[PropertyDefinition::BG_MONITORING_PROPERTY_PREFIX] || {}
384
+ monitoring_wrapper_props = @wrapper_props.dup
385
+
386
+ prefixed_wrapper_config.each do |key, value|
387
+ monitoring_wrapper_props[key] = value
388
+ end
389
+
390
+ # Ensure BG monitoring connections bypass BG routing.
391
+ monitoring_wrapper_props[BlueGreenPlugin::BG_SKIP_ROUTING_KEY] = true
392
+
393
+ monitoring_wrapper_props
394
+ end
395
+
396
+ # Driver overrides are merged onto base driver_props with defaults applied.
397
+ # @return Hash monitoring_driver_props
398
+ def build_monitoring_driver_props
399
+ prefixed_driver_props = @service_container.connection_service.prefixed_driver_config[PropertyDefinition::BG_MONITORING_PROPERTY_PREFIX] || {}
400
+ monitoring_driver_props = @service_container.connection_service.driver_props.dup
401
+
402
+ prefixed_driver_props.each do |key, value|
403
+ monitoring_driver_props[key] = value
404
+ end
405
+
406
+ monitoring_driver_props[:connect_timeout] ||= DEFAULT_CONNECT_TIMEOUT_SEC
407
+ monitoring_driver_props[:read_timeout] ||= DEFAULT_SOCKET_TIMEOUT_SEC unless @service_container.connection_service.pg?
408
+
409
+ monitoring_driver_props
410
+ end
411
+ end
412
+ end
413
+ end
414
+ end
@@ -0,0 +1,98 @@
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 '../../logging'
19
+ require_relative 'role'
20
+
21
+ module AwsAdvancedRubyDriverWrapper
22
+ module Plugins
23
+ module BlueGreen
24
+ # Tracks DNS and topology change milestones during a switchover.
25
+ # Fires callbacks when each milestone is first reached so StatusProvider can
26
+ # record events and trigger monitor resets at the right moments.
27
+ class SwitchoverState
28
+ include Logging
29
+
30
+ attr_reader :blue_dns_update_completed, :green_dns_removed, :green_topology_changed
31
+
32
+ def initialize(on_monitor_reset:, on_event:)
33
+ @on_monitor_reset = on_monitor_reset
34
+ @on_event = on_event
35
+
36
+ @blue_dns_update_completed = false
37
+ @green_dns_removed = false
38
+ @green_topology_changed = false
39
+
40
+ @monitor_reset_on_in_progress_completed = Concurrent::AtomicBoolean.new(false)
41
+ @monitor_reset_on_topology_completed = Concurrent::AtomicBoolean.new(false)
42
+ end
43
+
44
+ def update_dns_flags(bgd_id, role, interim_status)
45
+ if role == Role::SOURCE && !@blue_dns_update_completed && interim_status.all_start_topology_ip_changed
46
+ logger.debug { "[bgd_id: '#{bgd_id}'] Blue DNS update completed." }
47
+ @blue_dns_update_completed = true
48
+ @on_event.call('Blue DNS updated')
49
+ end
50
+
51
+ return unless role == Role::TARGET
52
+
53
+ if !@green_dns_removed && interim_status.all_start_topology_endpoints_removed
54
+ logger.debug { "[bgd_id: '#{bgd_id}'] Green DNS removed." }
55
+ @green_dns_removed = true
56
+ @on_event.call('Green DNS removed')
57
+ end
58
+
59
+ return unless !@green_topology_changed && interim_status.all_topology_changed
60
+
61
+ logger.debug { "[bgd_id: '#{bgd_id}'] Green topology changed." }
62
+ @green_topology_changed = true
63
+ @on_event.call('Green topology changed')
64
+ trigger_monitor_reset(@monitor_reset_on_topology_completed, '- green topology')
65
+ end
66
+
67
+ def trigger_in_progress_monitor_reset
68
+ trigger_monitor_reset(@monitor_reset_on_in_progress_completed, '- start')
69
+ end
70
+
71
+ def reset
72
+ @blue_dns_update_completed = false
73
+ @green_dns_removed = false
74
+ @green_topology_changed = false
75
+ @monitor_reset_on_in_progress_completed.make_false
76
+ @monitor_reset_on_topology_completed.make_false
77
+ end
78
+
79
+ def to_debug_s(iam_tracker)
80
+ " blue_dns_update_completed: #{@blue_dns_update_completed}\n " \
81
+ "green_dns_removed: #{@green_dns_removed}\n " \
82
+ "green_host_changed_name: #{iam_tracker.all_changed?}\n " \
83
+ "green_topology_changed: #{@green_topology_changed}"
84
+ end
85
+
86
+ private
87
+
88
+ def trigger_monitor_reset(flag, event_name)
89
+ return unless flag.false?
90
+ return unless flag.make_true
91
+
92
+ @on_monitor_reset.call(event_name)
93
+ @on_event.call("Monitors reset #{event_name}")
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,46 @@
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 BlueGreen
20
+ class SwitchoverTimer
21
+ def initialize(timeout_nano)
22
+ @timeout_nano = timeout_nano
23
+ @end_time_nano = 0
24
+ end
25
+
26
+ def start
27
+ @end_time_nano = nano_time + @timeout_nano if @end_time_nano.zero?
28
+ end
29
+
30
+ def expired?
31
+ @end_time_nano.positive? && @end_time_nano < nano_time
32
+ end
33
+
34
+ def reset
35
+ @end_time_nano = 0
36
+ end
37
+
38
+ private
39
+
40
+ def nano_time
41
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end