cfn_manage 0.1.3 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0f48d15f16f01693d8136bb57cdf406bfe9dca26
4
- data.tar.gz: 383c8c152a7a40748991ffdc79f89006a2c336df
3
+ metadata.gz: 69073fce6ae1e187369356d37b9180d21560b3de
4
+ data.tar.gz: 4d73c20f1873ec4abab56bf6b82bf429b6920995
5
5
  SHA512:
6
- metadata.gz: 3e4561f755c74c856c69054a20ce3e5850a51ce4cb78136d849e46ec7d634cf6568923bd5c943cefe4c5394840f02d195ca8e8f74e732cb5c31ccbfd0d976613
7
- data.tar.gz: 0a6dddd09ac36177ee2261eba24cecb455b8d1489f370f0c205c0c9d13d59b23c7a7f22841510613eeb0a1930eadaf214b0935b4296cfa084aa2a7d6a4317784
6
+ metadata.gz: 78e8e4f22bbc90e8ff23a6f9c0a521c5ef58085856ca85eccf8f52e8903b8121251105673d2e959a431be3d8eac9cb7ba0a46d3aa8dcf719948d8565f4f59c9b
7
+ data.tar.gz: c6fa186c4c1c14b39d54febf5c36b7e316b20ae2ed61e31da2aee16c7b5a2fbf0ae079a6d11447e06e5eefb198cb26952d05de98842f314a67ed9fa49bcada02
@@ -0,0 +1,39 @@
1
+ require_relative '../lib/aws_credentials'
2
+
3
+ module Base2
4
+
5
+ class AlarmStartStopHandler
6
+
7
+ def initialize(alarm_name)
8
+ @alarm_id = alarm_name
9
+ credentials = Base2::AWSCredentials.get_session_credentials("startstopalarm_#{@asg_name}")
10
+ @cwclient = Aws::CloudWatch::Client.new()
11
+ if credentials != nil
12
+ @cwclient = Aws::CloudWatch::Client.new(credentials: credentials)
13
+ end
14
+
15
+ @cwresource = Aws::CloudWatch::Resource.new(client: @cwclient)
16
+ @alarm = @cwresource.alarm(alarm_name)
17
+ end
18
+
19
+ def start(configuration)
20
+ if @alarm.actions_enabled
21
+ $log.info("Alarm #{@alarm.alarm_arn} actions already enabled")
22
+ return
23
+ end
24
+ $log.info("Enabling alarm #{@alarm.alarm_arn}")
25
+ @alarm.enable_actions({})
26
+ end
27
+
28
+ def stop
29
+ if not @alarm.actions_enabled
30
+ $log.info("Alarm #{@alarm.alarm_arn} actions already disabled")
31
+ return {}
32
+ end
33
+ $log.info("Disabling actions on alarm #{@alarm.alarm_arn}")
34
+ @alarm.disable_actions({})
35
+ return {}
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,70 @@
1
+ require_relative '../lib/aws_credentials'
2
+
3
+ module Base2
4
+
5
+ class AsgStartStopHandler
6
+
7
+ def initialize(asg_id)
8
+ @asg_name = asg_id
9
+
10
+ credentials = Base2::AWSCredentials.get_session_credentials("stopasg_#{@asg_name}")
11
+ @asg_client = Aws::AutoScaling::Client.new()
12
+ if credentials != nil
13
+ @asg_client = Aws::AutoScaling::Client.new(credentials: credentials)
14
+ end
15
+
16
+ asg_details = @asg_client.describe_auto_scaling_groups(
17
+ auto_scaling_group_names: [@asg_name]
18
+ )
19
+ if asg_details.auto_scaling_groups.size() == 0
20
+ raise "Couldn't find ASG #{@asg_name}"
21
+ end
22
+ @asg = asg_details.auto_scaling_groups[0]
23
+ end
24
+
25
+ def stop
26
+ # check if already stopped
27
+ if @asg.min_size == @asg.max_size and @asg.max_size == @asg.desired_capacity and @asg.min_size == 0
28
+ $log.info("ASG #{@asg_name} already stopped")
29
+ # nil and false configurations are not saved
30
+ return nil
31
+ else
32
+ # store asg configuration to S3
33
+ configuration = {
34
+ desired_capacity: @asg.desired_capacity,
35
+ min_size: @asg.min_size,
36
+ max_size: @asg.max_size
37
+ }
38
+
39
+ $log.info("Setting desired capacity to 0/0/0 for ASG #{@asg.auto_scaling_group_name}A")
40
+ # set asg configuration to 0/0/0
41
+ puts @asg.auto_scaling_group_name
42
+ @asg_client.update_auto_scaling_group({
43
+ auto_scaling_group_name: "#{@asg.auto_scaling_group_name}",
44
+ min_size: 0,
45
+ max_size: 0,
46
+ desired_capacity: 0
47
+ })
48
+ return configuration
49
+ end
50
+
51
+ end
52
+
53
+ def start(configuration)
54
+ if configuration.nil?
55
+ $log.warn("No configuration found for #{@asg_name}, skipping..")
56
+ return
57
+ end
58
+ $log.info("Starting ASG #{@asg_name} with following configuration\n#{configuration}")
59
+
60
+ # restore asg sizes
61
+ @asg_client.update_auto_scaling_group({
62
+ auto_scaling_group_name: @asg_name,
63
+ min_size: configuration['min_size'],
64
+ max_size: configuration['max_size'],
65
+ desired_capacity: configuration['desired_capacity']
66
+ })
67
+ end
68
+
69
+ end
70
+ end
@@ -3,6 +3,7 @@ require_relative '../lib/cf_common'
3
3
  require_relative '../lib/aws_credentials'
4
4
  require 'json'
5
5
  require 'yaml'
6
+ require_relative '../lib/start_stop_handler_factory'
6
7
 
7
8
  module Base2
8
9
  module CloudFormation
@@ -14,20 +15,14 @@ module Base2
14
15
  @s3_bucket = nil
15
16
  @credentials = nil
16
17
  @dry_run = false
17
- @@supported_start_stop_resources = {
18
- 'AWS::AutoScaling::AutoScalingGroup' => 'start_stop_asg',
19
- 'AWS::RDS::DBInstance' => 'start_stop_rds',
20
- 'AWS::EC2::Instance' => 'start_stop_ec2'
21
- }
22
18
 
23
19
  @@resource_start_priorities = {
24
20
  'AWS::RDS::DBInstance' => '100',
25
21
  'AWS::AutoScaling::AutoScalingGroup' => '200',
26
- 'AWS::EC2::Instance' => '200'
22
+ 'AWS::EC2::Instance' => '200',
23
+ 'AWS::CloudWatch::Alarm' => '300'
27
24
  }
28
25
 
29
- @environment_resources = nil
30
-
31
26
  def initialize()
32
27
  @environment_resources = []
33
28
  @s3_client = Aws::S3::Client.new()
@@ -35,7 +30,7 @@ module Base2
35
30
  @cf_client = Aws::CloudFormation::Client.new()
36
31
  @credentials = Base2::AWSCredentials.get_session_credentials('start_stop_environment')
37
32
  if not @credentials.nil?
38
- @cf_client = Aws::CloudFormation::Client.new(credentials: @credentials)
33
+ @cf_client = Aws::CloudFormation::Client.new(credentials: @credentials)
39
34
  end
40
35
  @dry_run = (ENV.key?('DRY_RUN') and ENV['DRY_RUN'] == '1')
41
36
  end
@@ -45,7 +40,7 @@ module Base2
45
40
  $log.info("Starting environment #{stack_name}")
46
41
  Common.visit_stack(@cf_client, stack_name, method(:collect_resources), true)
47
42
  do_start_assets
48
- configuration = {stack_running: true}
43
+ configuration = { stack_running: true }
49
44
  save_item_configuration("environment-data/stack-data/#{stack_name}", configuration) unless @dry_run
50
45
  $log.info("Environment #{stack_name} started")
51
46
  end
@@ -55,7 +50,7 @@ module Base2
55
50
  $log.info("Stopping environment #{stack_name}")
56
51
  Common.visit_stack(@cf_client, stack_name, method(:collect_resources), true)
57
52
  do_stop_assets
58
- configuration = {stack_running: false}
53
+ configuration = { stack_running: false }
59
54
  save_item_configuration("environment-data/stack-data/#{stack_name}", configuration) unless @dry_run
60
55
  $log.info("Environment #{stack_name} stopped")
61
56
  end
@@ -63,13 +58,17 @@ module Base2
63
58
 
64
59
  def do_stop_assets
65
60
  # sort start resource by priority
66
- @environment_resources = @environment_resources.sort_by { |k| k[:priority]}.reverse
61
+ @environment_resources = @environment_resources.sort_by { |k| k[:priority] }.reverse
67
62
 
68
63
  @environment_resources.each do |resource|
69
64
  $log.info("Stopping resource #{resource[:id]}")
70
65
  # just print out information if running a dry run, otherwise start assets
71
66
  if not @dry_run
72
- eval "self.#{resource[:method]}('stop','#{resource[:id]}')"
67
+ configuration = resource[:handler].stop()
68
+ if configuration.class == Hash
69
+ s3_prefix = "environment_data/resource/#{resource[:id]}"
70
+ save_item_configuration(s3_prefix, configuration)
71
+ end
73
72
  else
74
73
  $log.info("Dry run enabled, skipping stop start\nFollowing resource would be stopped: #{resource[:id]}")
75
74
  $log.debug("Resource type: #{resource[:type]}\n\n")
@@ -77,16 +76,20 @@ module Base2
77
76
  end
78
77
  end
79
78
 
80
-
81
79
  def do_start_assets
82
80
  # sort start resource by priority
83
- @environment_resources = @environment_resources.sort_by { |k| k[:priority]}
81
+ @environment_resources = @environment_resources.sort_by { |k| k[:priority] }
84
82
 
85
83
  @environment_resources.each do |resource|
86
84
  $log.info("Starting resource #{resource[:id]}")
87
85
  # just print out information if running a dry run, otherwise start assets
88
86
  if not @dry_run
89
- eval "self.#{resource[:method]}('start','#{resource[:id]}')"
87
+ # read configuration
88
+ s3_prefix = "environment_data/resource/#{resource[:id]}"
89
+ configuration = get_object_configuration(s3_prefix)
90
+
91
+ # start
92
+ resource[:handler].start(configuration)
90
93
  else
91
94
  $log.info("Dry run enabled, skipping actual start\nFollowing resource would be started: #{resource[:id]}")
92
95
  $log.debug("Resource type: #{resource[:type]}\n\n")
@@ -97,235 +100,28 @@ module Base2
97
100
  def collect_resources(stack_name)
98
101
  resrouces = @cf_client.describe_stack_resources(stack_name: stack_name)
99
102
  resrouces['stack_resources'].each do |resource|
100
- if @@supported_start_stop_resources.key?(resource['resource_type'])
101
- method_name = @@supported_start_stop_resources[resource['resource_type']]
103
+ start_stop_handler = nil
104
+ begin
105
+ start_stop_handler = Base2::StartStopHandlerFactory.get_start_stop_handler(
106
+ resource['resource_type'],
107
+ resource['physical_resource_id']
108
+ )
109
+ rescue Exception => e
110
+ $log.error("Error creating start-stop handler for resource of type #{resource['resource_type']}" +
111
+ "and with id #{resource['physical_resource_id']}:#{e}")
112
+ end
113
+ if not start_stop_handler.nil?
102
114
  resource_id = resource['physical_resource_id']
103
-
104
115
  @environment_resources << {
105
116
  id: resource_id,
106
117
  priority: @@resource_start_priorities[resource['resource_type']],
107
- method: method_name,
118
+ handler: start_stop_handler,
108
119
  type: resource['resource_type']
109
120
  }
110
121
  end
111
122
  end
112
123
  end
113
124
 
114
- def start_stop_ec2(cmd, instance_id)
115
- credentials = Base2::AWSCredentials.get_session_credentials("stoprun_#{instance_id}")
116
- ec2_client = Aws::EC2::Client.new(credentials: credentials)
117
-
118
- begin
119
- instance = Aws::EC2::Resource.new(client: ec2_client).instance(instance_id)
120
-
121
- if cmd == 'stop'
122
- if %w(stopped stopping).include?(instance.state.name)
123
- $log.info("Instance #{instance_id} already stopping or stopped")
124
- return
125
- end
126
- $log.info("Stopping instance #{instance_id}")
127
- instance.stop()
128
- end
129
-
130
- if cmd == 'start'
131
- if %w(running).include?(instance.state.name)
132
- $log.info("Instance #{instance_id} already running")
133
- return
134
- end
135
- $log.info("Starting instance #{instance_id}")
136
- instance.start()
137
- end
138
- rescue => e
139
- $log.error("Failed execution #{cmd} on instance #{instance_id}:\n#{e}")
140
- end
141
-
142
- end
143
-
144
- def start_stop_asg(cmd, asg_name)
145
-
146
- # read asg data
147
- credentials = Base2::AWSCredentials.get_session_credentials("stopasg_#{asg_name}")
148
- asg_client = Aws::AutoScaling::Client.new()
149
- if credentials != nil
150
- asg_client = Aws::AutoScaling::Client.new(credentials: credentials)
151
- end
152
-
153
- asg_details = asg_client.describe_auto_scaling_groups(
154
- auto_scaling_group_names: [asg_name]
155
- )
156
- if asg_details.auto_scaling_groups.size() == 0
157
- raise "Couldn't find ASG #{asg_name}"
158
- end
159
- asg = asg_details.auto_scaling_groups[0]
160
- s3_prefix = "environment-data/asg-data/#{asg_name}"
161
- case cmd
162
- when 'start'
163
-
164
- # retrieve asg params from s3
165
- configuration = self.get_object_configuration(s3_prefix)
166
- if configuration.nil?
167
- $log.warn("No configuration found for #{asg_name}, skipping..")
168
- return
169
- end
170
- $log.info("Starting ASG #{asg_name} with following configuration\n#{configuration}")
171
-
172
- # restore asg sizes
173
- asg_client.update_auto_scaling_group({
174
- auto_scaling_group_name: asg_name,
175
- min_size: configuration['min_size'],
176
- max_size: configuration['max_size'],
177
- desired_capacity: configuration['desired_capacity']
178
- })
179
-
180
- when 'stop'
181
- # check if already stopped
182
- if asg.min_size == asg.max_size and asg.max_size == asg.desired_capacity and asg.min_size == 0
183
- $log.info("ASG #{asg_name} already stopped")
184
- else
185
- # store asg configuration to S3
186
- configuration = {
187
- desired_capacity: asg.desired_capacity,
188
- min_size: asg.min_size,
189
- max_size: asg.max_size
190
- }
191
- self.save_item_configuration(s3_prefix, configuration)
192
-
193
- $log.info("Setting desired capacity to 0/0/0 for ASG #{asg_name}")
194
- # set asg configuration to 0/0/0
195
- asg_client.update_auto_scaling_group({
196
- auto_scaling_group_name: asg_name,
197
- min_size: 0,
198
- max_size: 0,
199
- desired_capacity: 0
200
- })
201
- end
202
- # TODO wait for operation to complete (optionally)
203
- end
204
-
205
-
206
- end
207
-
208
- def start_stop_rds(cmd, instance_id)
209
- credentials = Base2::AWSCredentials.get_session_credentials("startstoprds_#{instance_id}")
210
- rds_client = Aws::RDS::Client.new()
211
- if credentials != nil
212
- rds_client = Aws::RDS::Client.new(credentials: credentials)
213
- end
214
- rds = Aws::RDS::Resource.new(client: rds_client)
215
- rds_instance = rds.db_instance(instance_id)
216
- s3_prefix = "environment-data/rds-data/#{instance_id}"
217
- case cmd
218
- when 'start'
219
- if rds_instance.db_instance_status == 'available'
220
- $log.info("RDS Instance #{instance_id} is already in available state")
221
- return
222
- end
223
-
224
- #retrieve multi-az data from S3
225
- configuration = get_object_configuration(s3_prefix)
226
- if configuration.nil?
227
- $log.warning("No configuration found for #{rds_instance}, skipping..")
228
- return
229
- end
230
-
231
- # start rds instance
232
- if rds_instance.db_instance_status == 'stopped'
233
- $log.info("Starting db instance #{instance_id}")
234
- rds_client.start_db_instance({ db_instance_identifier: instance_id })
235
-
236
- # wait instance to become available
237
- $log.info("Waiting db instance to become available #{instance_id}")
238
- wait_rds_instance_states(rds_client, instance_id, %w(starting available))
239
- else
240
- wait_rds_instance_states(rds_client, instance_id, %w(available))
241
- end
242
-
243
- # convert rds instance to mutli-az if required
244
- if configuration['is_multi_az']
245
- $log.info("Converting to Multi-AZ instance after start (instance #{instance_id})")
246
- set_rds_instance_multi_az(rds_instance, true)
247
- end
248
-
249
- when 'stop'
250
- # store mutli-az data to S3
251
- if rds_instance.db_instance_status != 'available'
252
- $log.warn("RDS Instance #{instance_id} not in available state, and thus can not be stopped")
253
- $log.warn("RDS Instance #{instance_id} state: #{rds_instance.db_instance_status}")
254
- return
255
- end
256
-
257
- if rds_instance.db_instance_status == 'stopped'
258
- $log.info("RDS Instance #{instance_id} is already stopped")
259
- return
260
- end
261
-
262
- # RDS stop start does not support Aurora yet. Ignore if engine is aurora
263
- if rds_instance.engine == 'aurora'
264
- $log.info("RDS Instance #{instance_id} engine is aurora and cannot be stoped yet...")
265
- return
266
- end
267
-
268
- configuration = {
269
- is_multi_az: rds_instance.multi_az
270
- }
271
- save_item_configuration(s3_prefix, configuration)
272
-
273
- #check if mutli-az RDS. if so, convert to single-az
274
- if rds_instance.multi_az
275
- $log.info("Converting to Non-Multi-AZ instance before stop (instance #{instance_id}")
276
- set_rds_instance_multi_az(rds_instance, false)
277
- end
278
-
279
- # stop rds instance and wait for it to be fully stopped
280
- $log.info("Stopping instance #{instance_id}")
281
- rds_client.stop_db_instance({ db_instance_identifier: instance_id })
282
- $log.info("Waiting db instance to be stopped #{instance_id}")
283
- wait_rds_instance_states(rds_client, instance_id, %w(stopping stopped))
284
- end
285
- end
286
-
287
- def set_rds_instance_multi_az(rds_instance, multi_az)
288
- if rds_instance.multi_az == multi_az
289
- $log.info("Rds instance #{rds_instance.db_instance_identifier} already multi-az=#{multi_az}")
290
- return
291
- end
292
- rds_instance.modify({ multi_az: multi_az, apply_immediately: true })
293
- # allow half an hour for instance to be converted
294
- wait_states = %w(modifying available)
295
- wait_rds_instance_states(rds_instance, wait_states)
296
- end
297
-
298
- def wait_rds_instance_states(client, rds_instance_id, wait_states)
299
- wait_states.each do |state|
300
- # reached state must be steady, at least a minute. Modifying an instance to/from MultiAZ can't be shorter
301
- # than 40 seconds, hence steady count is 4
302
- state_count = 0
303
- steady_count = 4
304
- attempts = 0
305
- rds = Aws::RDS::Resource.new(client: client)
306
- until attempts == (max_attempts = 60*6) do
307
- instance = rds.db_instance(rds_instance_id)
308
- $log.info("Instance #{instance.db_instance_identifier} state: #{instance.db_instance_status}, waiting for #{state}")
309
-
310
- if instance.db_instance_status == "#{state}"
311
- state_count = state_count + 1
312
- $log.info("#{state_count}/#{steady_count}")
313
- else
314
- state_count = 0
315
- end
316
- break if state_count == steady_count
317
- attempts = attempts + 1
318
- sleep(15)
319
- end
320
-
321
- if attempts == max_attempts
322
- $log.error("RDS Database Instance #{rds_instance_id} did not enter #{state} state, however continuing operations...")
323
- end
324
-
325
- end
326
- end
327
-
328
-
329
125
  def get_object_configuration(s3_prefix)
330
126
  configuration = nil
331
127
  begin
@@ -358,6 +154,9 @@ module Base2
358
154
  })
359
155
  end
360
156
  end
157
+
158
+ private :do_stop_assets, :do_start_assets, :collect_resources, :get_object_configuration, :save_item_configuration
159
+
361
160
  end
362
161
  end
363
162
  end
@@ -0,0 +1,41 @@
1
+ require_relative '../lib/aws_credentials'
2
+
3
+ module Base2
4
+
5
+ class Ec2StartStopHandler
6
+
7
+ @instance
8
+
9
+ def initialize(instance_id)
10
+ credentials = Base2::AWSCredentials.get_session_credentials("stoprun_#{instance_id}")
11
+ ec2_client = Aws::EC2::Client.new(credentials: credentials)
12
+ @instance = Aws::EC2::Resource.new(client: ec2_client).instance(instance_id)
13
+ @instance_id = instance_id
14
+ end
15
+
16
+ def start(configuration)
17
+ if %w(running).include?(@instance.state.name)
18
+ $log.info("Instance #{@instance_id} already running")
19
+ return
20
+ end
21
+ $log.info("Starting instance #{@instance_id}")
22
+ @instance.start()
23
+ end
24
+
25
+ def stop
26
+ if %w(stopped stopping).include?(@instance.state.name)
27
+ $log.info("Instance #{@instance_id} already stopping or stopped")
28
+ return
29
+ end
30
+ $log.info("Stopping instance #{@instance_id}")
31
+ @instance.stop()
32
+
33
+ # empty configuration for ec2 instances
34
+ return {}
35
+ end
36
+
37
+
38
+
39
+
40
+ end
41
+ end
@@ -0,0 +1,128 @@
1
+ require_relative '../lib/aws_credentials'
2
+
3
+ module Base2
4
+
5
+ class RdsStartStopHandler
6
+
7
+ def initialize(instance_id)
8
+ @instance_id = instance_id
9
+
10
+ credentials = Base2::AWSCredentials.get_session_credentials("startstoprds_#{instance_id}")
11
+ @rds_client = Aws::RDS::Client.new()
12
+ if credentials != nil
13
+ @rds_client = Aws::RDS::Client.new(credentials: credentials)
14
+ end
15
+ rds = Aws::RDS::Resource.new(client: @rds_client)
16
+ @rds_instance = rds.db_instance(instance_id)
17
+
18
+ end
19
+
20
+ def start(configuration)
21
+ if @rds_instance.db_instance_status == 'available'
22
+ $log.info("RDS Instance #{@instance_id} is already in available state")
23
+ return
24
+ end
25
+
26
+ # start rds instance
27
+ if @rds_instance.db_instance_status == 'stopped'
28
+ $log.info("Starting db instance #{@instance_id}")
29
+ @rds_client.start_db_instance({ db_instance_identifier: @instance_id })
30
+
31
+ # wait instance to become available
32
+ $log.info("Waiting db instance to become available #{@instance_id}")
33
+ wait_rds_instance_states( %w(starting available))
34
+ else
35
+ wait_rds_instance_states( %w(available))
36
+ end
37
+
38
+ # convert rds instance to mutli-az if required
39
+ if configuration['is_multi_az']
40
+ $log.info("Converting to Multi-AZ instance after start (instance #{@instance_id})")
41
+ set_rds_instance_multi_az( true)
42
+ end
43
+ end
44
+
45
+ def stop
46
+
47
+ configuration = {
48
+ is_multi_az: @rds_instance.multi_az
49
+ }
50
+ # RDS stop start does not support Aurora yet. Ignore if engine is aurora
51
+ if @rds_instance.engine == 'aurora'
52
+ $log.info("RDS Instance #{instance_id} engine is aurora and cannot be stoped yet...")
53
+ return configuration
54
+ end
55
+
56
+ # check if available
57
+ if @rds_instance.db_instance_status != 'available'
58
+ $log.warn("RDS Instance #{@instance_id} not in available state, and thus can not be stopped")
59
+ $log.warn("RDS Instance #{@instance_id} state: #{@rds_instance.db_instance_status}")
60
+ return configuration
61
+ end
62
+
63
+ # check if already stopped
64
+ if @rds_instance.db_instance_status == 'stopped'
65
+ $log.info("RDS Instance #{@instance_id} is already stopped")
66
+ return configuration
67
+ end
68
+
69
+ #check if mutli-az RDS. if so, convert to single-az
70
+ if @rds_instance.multi_az
71
+ $log.info("Converting to Non-Multi-AZ instance before stop (instance #{@instance_id}")
72
+ set_rds_instance_multi_az(false)
73
+ end
74
+
75
+ # stop rds instance and wait for it to be fully stopped
76
+ $log.info("Stopping instance #{@instance_id}")
77
+ @rds_client.stop_db_instance({ db_instance_identifier: @instance_id })
78
+ $log.info("Waiting db instance to be stopped #{@instance_id}")
79
+ wait_rds_instance_states(%w(stopping stopped))
80
+
81
+ return configuration
82
+ end
83
+
84
+ def set_rds_instance_multi_az(multi_az)
85
+ if @rds_instance.multi_az == multi_az
86
+ $log.info("Rds instance #{@rds_instance.db_instance_identifier} already multi-az=#{multi_az}")
87
+ return
88
+ end
89
+ @rds_instance.modify({ multi_az: multi_az, apply_immediately: true })
90
+ # allow half an hour for instance to be converted
91
+ wait_states = %w(modifying available)
92
+ self.wait_rds_instance_states( wait_states)
93
+ end
94
+
95
+ def wait_rds_instance_states(wait_states)
96
+ wait_states.each do |state|
97
+ # reached state must be steady, at least a minute. Modifying an instance to/from MultiAZ can't be shorter
98
+ # than 40 seconds, hence steady count is 4
99
+ state_count = 0
100
+ steady_count = 4
101
+ attempts = 0
102
+ rds = Aws::RDS::Resource.new(client: @rds_client)
103
+ until attempts == (max_attempts = 60*6) do
104
+ instance = rds.db_instance(@instance_id)
105
+ $log.info("Instance #{instance.db_instance_identifier} state: #{instance.db_instance_status}, waiting for #{state}")
106
+
107
+ if instance.db_instance_status == "#{state}"
108
+ state_count = state_count + 1
109
+ $log.info("#{state_count}/#{steady_count}")
110
+ else
111
+ state_count = 0
112
+ end
113
+ break if state_count == steady_count
114
+ attempts = attempts + 1
115
+ sleep(15)
116
+ end
117
+
118
+ if attempts == max_attempts
119
+ $log.error("RDS Database Instance #{@instance_id} did not enter #{state} state, however continuing operations...")
120
+ end
121
+ end
122
+ end
123
+
124
+ private :set_rds_instance_multi_az, :wait_rds_instance_states
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,32 @@
1
+ require_relative '../lib/asg_start_stop_handler'
2
+ require_relative '../lib/ec2_start_stop_handler'
3
+ require_relative '../lib/rds_start_stop_handler'
4
+ require_relative '../lib/alarm_start_stop_handler'
5
+
6
+ module Base2
7
+
8
+ class StartStopHandlerFactory
9
+
10
+ # Factory method to get start/stop handler based on CloudFormation
11
+ # resource type. If resource_id passed in does not exist, it is
12
+ # very likely that exception will be raised
13
+ def self.get_start_stop_handler(resource_type, resource_id)
14
+ case resource_type
15
+ when 'AWS::AutoScaling::AutoScalingGroup'
16
+ return Base2::AsgStartStopHandler.new(resource_id)
17
+
18
+ when 'AWS::EC2::Instance'
19
+ return Base2::Ec2StartStopHandler.new(resource_id)
20
+
21
+ when 'AWS::RDS::DBInstance'
22
+ return Base2::RdsStartStopHandler.new(resource_id)
23
+
24
+ when 'AWS::CloudWatch::Alarm'
25
+ return Base2::AlarmStartStopHandler.new(resource_id)
26
+
27
+ else
28
+ return nil
29
+ end
30
+ end
31
+ end
32
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfn_manage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Base2Services
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-09-26 00:00:00.000000000 Z
12
+ date: 2017-10-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -41,10 +41,15 @@ files:
41
41
  - bin/cfn_manage
42
42
  - bin/cfn_manage.rb
43
43
  - bin/usage.txt
44
+ - lib/alarm_start_stop_handler.rb
45
+ - lib/asg_start_stop_handler.rb
44
46
  - lib/aws_credentials.rb
45
47
  - lib/cf_common.rb
46
48
  - lib/cf_progress_tracker.rb
47
49
  - lib/cf_start_stop_environment.rb
50
+ - lib/ec2_start_stop_handler.rb
51
+ - lib/rds_start_stop_handler.rb
52
+ - lib/start_stop_handler_factory.rb
48
53
  homepage: https://github.com/base2Services/cfn-library/blob/master/README.md
49
54
  licenses:
50
55
  - MIT