cfn-guardian 0.4.0 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build-gem.yml +25 -0
  3. data/.github/workflows/release-gem.yml +25 -0
  4. data/.github/workflows/release-image.yml +33 -0
  5. data/.rspec +1 -0
  6. data/Gemfile.lock +13 -13
  7. data/README.md +3 -819
  8. data/cfn-guardian.gemspec +1 -3
  9. data/docs/alarm_templates.md +130 -0
  10. data/docs/cli.md +182 -0
  11. data/docs/composite_alarms.md +24 -0
  12. data/docs/custom_checks/azure_file_check.md +28 -0
  13. data/docs/custom_checks/domain_expiry.md +10 -0
  14. data/docs/custom_checks/http.md +59 -0
  15. data/docs/custom_checks/log_group_metric_filters.md +27 -0
  16. data/docs/custom_checks/nrpe.md +29 -0
  17. data/docs/custom_checks/port.md +40 -0
  18. data/docs/custom_checks/sftp.md +73 -0
  19. data/docs/custom_checks/sql.md +44 -0
  20. data/docs/custom_checks/tls.md +25 -0
  21. data/docs/custom_metrics.md +71 -0
  22. data/docs/event_subscriptions.md +67 -0
  23. data/docs/maintenance_mode.md +85 -0
  24. data/docs/notifiers.md +33 -0
  25. data/docs/overview.md +22 -0
  26. data/docs/resources.md +93 -0
  27. data/docs/variables.md +58 -0
  28. data/lib/cfnguardian.rb +72 -58
  29. data/lib/cfnguardian/cloudwatch.rb +43 -32
  30. data/lib/cfnguardian/compile.rb +82 -5
  31. data/lib/cfnguardian/deploy.rb +2 -16
  32. data/lib/cfnguardian/display_formatter.rb +1 -2
  33. data/lib/cfnguardian/error.rb +4 -0
  34. data/lib/cfnguardian/models/alarm.rb +40 -28
  35. data/lib/cfnguardian/models/check.rb +30 -12
  36. data/lib/cfnguardian/models/event.rb +43 -15
  37. data/lib/cfnguardian/models/event_subscription.rb +96 -0
  38. data/lib/cfnguardian/resources/azure_file.rb +20 -0
  39. data/lib/cfnguardian/resources/base.rb +111 -26
  40. data/lib/cfnguardian/resources/ec2_instance.rb +11 -0
  41. data/lib/cfnguardian/resources/http.rb +1 -0
  42. data/lib/cfnguardian/resources/rds_cluster.rb +14 -0
  43. data/lib/cfnguardian/resources/rds_instance.rb +71 -0
  44. data/lib/cfnguardian/stacks/main.rb +7 -6
  45. data/lib/cfnguardian/stacks/resources.rb +34 -5
  46. data/lib/cfnguardian/version.rb +1 -1
  47. metadata +35 -10
@@ -2,7 +2,7 @@ require 'cfnguardian/string'
2
2
 
3
3
  module CfnGuardian
4
4
  module Models
5
- class Check
5
+ class BaseCheck
6
6
 
7
7
  attr_reader :type
8
8
  attr_accessor :group,
@@ -13,7 +13,9 @@ module CfnGuardian
13
13
  :runtime,
14
14
  :environment,
15
15
  :subnets,
16
- :vpc
16
+ :vpc,
17
+ :memory,
18
+ :timeout
17
19
 
18
20
  def initialize(resource)
19
21
  @type = 'Check'
@@ -26,17 +28,19 @@ module CfnGuardian
26
28
  @environment = ''
27
29
  @subnets = nil
28
30
  @vpc = nil
31
+ @memory = 128
32
+ @timeout = 120
29
33
  end
30
34
  end
31
35
 
32
- class HttpCheck < Check
36
+ class HttpCheck < BaseCheck
33
37
  def initialize(resource)
34
38
  super(resource)
35
39
  @group = 'Http'
36
40
  @name = 'HttpCheck'
37
41
  @package = 'http-check'
38
42
  @handler = 'handler.http_check'
39
- @version = '0bc33e51abb1f27729ecb170611bf6b440e71a0e'
43
+ @version = 'f739631de74f1a882163b7e584a8b4710ccbc134'
40
44
  @runtime = 'python3.7'
41
45
  end
42
46
  end
@@ -52,7 +56,7 @@ module CfnGuardian
52
56
  end
53
57
  end
54
58
 
55
- class PortCheck < Check
59
+ class PortCheck < BaseCheck
56
60
  def initialize(resource)
57
61
  super(resource)
58
62
  @group = 'Port'
@@ -75,7 +79,7 @@ module CfnGuardian
75
79
  end
76
80
  end
77
81
 
78
- class NrpeCheck < Check
82
+ class NrpeCheck < BaseCheck
79
83
  def initialize(resource)
80
84
  super(resource)
81
85
  @group = 'Nrpe'
@@ -90,7 +94,7 @@ module CfnGuardian
90
94
  end
91
95
  end
92
96
 
93
- class SslCheck < Check
97
+ class SslCheck < BaseCheck
94
98
  def initialize(resource)
95
99
  super(resource)
96
100
  @group = 'Ssl'
@@ -113,7 +117,7 @@ module CfnGuardian
113
117
  end
114
118
  end
115
119
 
116
- class DomainExpiryCheck < Check
120
+ class DomainExpiryCheck < BaseCheck
117
121
  def initialize(resource)
118
122
  super(resource)
119
123
  @group = 'DomainExpiry'
@@ -125,7 +129,7 @@ module CfnGuardian
125
129
  end
126
130
  end
127
131
 
128
- class SqlCheck < Check
132
+ class SqlCheck < BaseCheck
129
133
  def initialize(resource)
130
134
  super(resource)
131
135
  @group = 'Sql'
@@ -140,7 +144,7 @@ module CfnGuardian
140
144
  end
141
145
  end
142
146
 
143
- class ContainerInstanceCheck < Check
147
+ class ContainerInstanceCheck < BaseCheck
144
148
  def initialize(resource)
145
149
  super(resource)
146
150
  @group = 'ContainerInstance'
@@ -152,7 +156,7 @@ module CfnGuardian
152
156
  end
153
157
  end
154
158
 
155
- class TLSCheck < Check
159
+ class TLSCheck < BaseCheck
156
160
  def initialize(resource)
157
161
  super(resource)
158
162
  @group = 'TLS'
@@ -164,7 +168,7 @@ module CfnGuardian
164
168
  end
165
169
  end
166
170
 
167
- class SFTPCheck < Check
171
+ class SFTPCheck < BaseCheck
168
172
  def initialize(resource)
169
173
  super(resource)
170
174
  @group = 'SFTP'
@@ -187,5 +191,19 @@ module CfnGuardian
187
191
  end
188
192
  end
189
193
 
194
+ class AzureFileCheck < BaseCheck
195
+ def initialize(resource)
196
+ super(resource)
197
+ @group = 'AzureFile'
198
+ @name = 'AzureFileCheck'
199
+ @package = 'azure-file-check'
200
+ @handler = 'handler.file_check'
201
+ @version = 'cc37aa8fe4855570132431611b507274b390f4c1'
202
+ @runtime = 'python3.7'
203
+ @memory = 256
204
+ @timeout = 600
205
+ end
206
+ end
207
+
190
208
  end
191
209
  end
@@ -2,7 +2,7 @@ require 'cfnguardian/string'
2
2
 
3
3
  module CfnGuardian
4
4
  module Models
5
- class Event
5
+ class BaseEvent
6
6
 
7
7
  attr_reader :type
8
8
  attr_accessor :group,
@@ -31,7 +31,7 @@ module CfnGuardian
31
31
  end
32
32
  end
33
33
 
34
- class HttpEvent < Event
34
+ class HttpEvent < BaseEvent
35
35
 
36
36
  attr_accessor :endpoint,
37
37
  :method,
@@ -81,7 +81,7 @@ module CfnGuardian
81
81
  end
82
82
  end
83
83
 
84
- class PortEvent < Event
84
+ class PortEvent < BaseEvent
85
85
  def initialize(resource)
86
86
  super(resource)
87
87
  @group = 'Port'
@@ -112,7 +112,7 @@ module CfnGuardian
112
112
  end
113
113
  end
114
114
 
115
- class NrpeEvent < Event
115
+ class NrpeEvent < BaseEvent
116
116
  def initialize(resource,environment,command)
117
117
  super(resource)
118
118
  @group = 'Nrpe'
@@ -121,6 +121,7 @@ module CfnGuardian
121
121
  @host = resource['Id']
122
122
  @environment = environment
123
123
  @region = resource.fetch('Region',"${AWS::Region}")
124
+ @hash = Digest::MD5.hexdigest "#{resource['Id']}#{command}"
124
125
  @command = command
125
126
  end
126
127
 
@@ -134,13 +135,13 @@ module CfnGuardian
134
135
  end
135
136
  end
136
137
 
137
- class SslEvent < Event
138
+ class SslEvent < BaseEvent
138
139
  def initialize(resource)
139
140
  super(resource)
140
141
  @group = 'Ssl'
141
142
  @name = 'SslEvent'
142
143
  @target = 'SslCheckFunction'
143
- @cron = "0 12 * * ? *"
144
+ @cron = resource.fetch('Schedule', "0 12 * * ? *")
144
145
  @url = resource['Id']
145
146
  @region = resource.fetch('Region',"${AWS::Region}")
146
147
  end
@@ -163,7 +164,7 @@ module CfnGuardian
163
164
  end
164
165
  end
165
166
 
166
- class DomainExpiryEvent < Event
167
+ class DomainExpiryEvent < BaseEvent
167
168
 
168
169
  attr_accessor :domain,
169
170
  :region
@@ -173,7 +174,7 @@ module CfnGuardian
173
174
  @group = 'DomainExpiry'
174
175
  @name = 'DomainExpiryEvent'
175
176
  @target = 'DomainExpiryCheckFunction'
176
- @cron = "0 12 * * ? *"
177
+ @cron = resource.fetch('Schedule', "0 12 * * ? *")
177
178
  @domain = resource['Id']
178
179
  @region = resource.fetch('Region',"${AWS::Region}")
179
180
  end
@@ -183,7 +184,7 @@ module CfnGuardian
183
184
  end
184
185
  end
185
186
 
186
- class SqlEvent < Event
187
+ class SqlEvent < BaseEvent
187
188
  def initialize(resource,query,environment)
188
189
  super(resource)
189
190
  @group = 'Sql'
@@ -221,13 +222,13 @@ module CfnGuardian
221
222
  end
222
223
  end
223
224
 
224
- class ContainerInstanceEvent < Event
225
+ class ContainerInstanceEvent < BaseEvent
225
226
  def initialize(resource)
226
227
  super(resource)
227
228
  @group = 'ContainerInstance'
228
229
  @name = 'ContainerInstanceEvent'
229
230
  @target = 'ContainerInstanceCheckFunction'
230
- @cron = "0/5 * * * ? *"
231
+ @cron = resource.fetch('Schedule', "0/5 * * * ? *")
231
232
  @cluster = resource['Id']
232
233
  end
233
234
 
@@ -236,13 +237,13 @@ module CfnGuardian
236
237
  end
237
238
  end
238
239
 
239
- class SFTPEvent < Event
240
+ class SFTPEvent < BaseEvent
240
241
  def initialize(resource)
241
242
  super(resource)
242
243
  @group = 'SFTP'
243
244
  @name = 'SFTPEvent'
244
245
  @target = 'SFTPCheckFunction'
245
- @cron = "0/5 * * * ? *"
246
+ @cron = resource.fetch('Schedule', "0/5 * * * ? *")
246
247
  @host = resource['Id']
247
248
  @user = resource['User']
248
249
  @port = resource.fetch('Port', nil)
@@ -288,13 +289,13 @@ module CfnGuardian
288
289
  end
289
290
  end
290
291
 
291
- class TLSEvent < Event
292
+ class TLSEvent < BaseEvent
292
293
  def initialize(resource)
293
294
  super(resource)
294
295
  @group = 'TLS'
295
296
  @name = 'TLSEvent'
296
297
  @target = 'TLSCheckFunction'
297
- @cron = "0/5 * * * ? *"
298
+ @cron = resource.fetch('Schedule', "0/5 * * * ? *")
298
299
  @host = resource['Id']
299
300
  @port = resource.fetch('Port', 443)
300
301
  @check_max = resource.fetch('MaxSupported', nil)
@@ -312,5 +313,32 @@ module CfnGuardian
312
313
  end
313
314
  end
314
315
 
316
+ class AzureFileEvent < BaseEvent
317
+ def initialize(resource)
318
+ super(resource)
319
+ @group = 'AzureFile'
320
+ @name = 'AzureFileEvent'
321
+ @target = 'AzureFileCheckFunction'
322
+ @cron = resource.fetch('Schedule', "0/5 * * * ? *")
323
+ @storage_account = resource['Id']
324
+ @container = resource['Container']
325
+ @connection_string = resource['ConnectionString']
326
+ @search = resource['Search']
327
+ end
328
+
329
+ def payload
330
+ return {
331
+ 'STORAGE_ACCOUNT' => @storage_account,
332
+ 'CONTAINER' => @container,
333
+ 'CONNECTION_STRING' => @connection_string,
334
+ 'SEARCH' => @search
335
+ }.to_json
336
+ end
337
+
338
+ def ssm_parameters
339
+ return [@connection_string]
340
+ end
341
+ end
342
+
315
343
  end
316
344
  end
@@ -0,0 +1,96 @@
1
+ module CfnGuardian
2
+ module Models
3
+ class BaseEventSubscription
4
+
5
+ attr_reader :type, :group
6
+ attr_writer :detail
7
+ attr_accessor :name,
8
+ :enabled,
9
+ :hash,
10
+ :topic,
11
+ :resource_id,
12
+ :resource_arn,
13
+ :source,
14
+ :detail_type,
15
+ :detail
16
+
17
+ def initialize(resource)
18
+ @type = 'EventSubscription'
19
+ @group = self.class.name.split('::').last
20
+ @name = ''
21
+ @hash = Digest::MD5.hexdigest resource['Id']
22
+ @enabled = true
23
+ @events = []
24
+ @topic = 'Events'
25
+ @resource_id = resource['Id']
26
+ @resource_arn = ''
27
+ @source = ''
28
+ @detail_type = ''
29
+ @detail = {}
30
+ end
31
+
32
+ def detail
33
+ return @detail
34
+ end
35
+ end
36
+
37
+ class RDSEventSubscription < BaseEventSubscription
38
+ attr_accessor :source_id, :rds_event_category, :message
39
+
40
+ def initialize(resource)
41
+ super(resource)
42
+ @source = 'aws.rds'
43
+ @detail_type = 'RDS DB Instance Event'
44
+ @source_id = ''
45
+ @rds_event_category = ''
46
+ @message = ''
47
+ end
48
+
49
+ def detail
50
+ return {
51
+ EventCategories: [@rds_event_category],
52
+ SourceType: [@source_type],
53
+ SourceIdentifier: ["rds:#{@resource_id}"],
54
+ Message: [@message]
55
+ }
56
+ end
57
+ end
58
+
59
+ class RDSInstanceEventSubscription < RDSEventSubscription
60
+ def initialize(resource)
61
+ super(resource)
62
+ @source_type = 'DB_INSTANCE'
63
+ end
64
+ end
65
+
66
+ class RDSClusterEventSubscription < RDSEventSubscription
67
+ def initialize(resource)
68
+ super(resource)
69
+ @source_type = 'DB_CLUSTER'
70
+ end
71
+ end
72
+
73
+ class Ec2InstanceEventSubscription < BaseEventSubscription
74
+ def initialize(resource)
75
+ super(resource)
76
+ @source = 'aws.ec2'
77
+ end
78
+ end
79
+
80
+ class ApiGatewayEventSubscription < BaseEventSubscription; end
81
+ class ApplicationTargetGroupEventSubscription < BaseEventSubscription; end
82
+ class AmazonMQBrokerEventSubscription < BaseEventSubscription; end
83
+ class CloudFrontDistributionEventSubscription < BaseEventSubscription; end
84
+ class AutoScalingGroupEventSubscription < BaseEventSubscription; end
85
+ class DynamoDBTableEventSubscription < BaseEventSubscription; end
86
+ class Ec2InstanceEventSubscription < BaseEventSubscription; end
87
+ class ECSClusterEventSubscription < BaseEventSubscription; end
88
+ class ECSServiceEventSubscription < BaseEventSubscription; end
89
+ class ElastiCacheReplicationGroupEventSubscription < BaseEventSubscription; end
90
+ class ElasticLoadBalancerEventSubscription < BaseEventSubscription; end
91
+ class ElasticFileSystemEventSubscription < BaseEventSubscription; end
92
+ class LambdaEventSubscription < BaseEventSubscription; end
93
+ class NetworkTargetGroupEventSubscription < BaseEventSubscription; end
94
+ class RedshiftClusterEventSubscription < BaseEventSubscription; end
95
+ end
96
+ end
@@ -0,0 +1,20 @@
1
+ module CfnGuardian::Resource
2
+ class AzureFile < Base
3
+
4
+ def default_alarms
5
+ alarm = CfnGuardian::Models::AzureFileAlarm.new(@resource)
6
+ alarm.name = 'FileExpired'
7
+ alarm.metric_name = 'FileExpired'
8
+ @alarms.push(alarm)
9
+ end
10
+
11
+ def default_events
12
+ @events.push(CfnGuardian::Models::AzureFileEvent.new(@resource))
13
+ end
14
+
15
+ def default_checks
16
+ @checks.push(CfnGuardian::Models::AzureFileCheck.new(@resource))
17
+ end
18
+
19
+ end
20
+ end
@@ -4,6 +4,7 @@ require 'cfnguardian/models/alarm'
4
4
  require 'cfnguardian/models/event'
5
5
  require 'cfnguardian/models/check'
6
6
  require 'cfnguardian/models/metric_filter'
7
+ require 'cfnguardian/models/event_subscription'
7
8
 
8
9
  module CfnGuardian::Resource
9
10
  class Base
@@ -16,6 +17,7 @@ module CfnGuardian::Resource
16
17
  @events = []
17
18
  @checks = []
18
19
  @metric_filters = []
20
+ @event_subscriptions = []
19
21
  end
20
22
 
21
23
  # Overidden by inheritted classes to define default alarms
@@ -23,7 +25,7 @@ module CfnGuardian::Resource
23
25
  return @alarms
24
26
  end
25
27
 
26
- def get_alarms(resource,group,overides={})
28
+ def get_alarms(group,overides={})
27
29
  # generate default alarms
28
30
  default_alarms()
29
31
 
@@ -32,20 +34,22 @@ module CfnGuardian::Resource
32
34
  overides.delete('GroupOverrides')
33
35
  if group_overrides.any?
34
36
  @alarms.each do |alarm|
35
- group_overrides.each {|attr,value| update_alarm(alarm,attr,value)}
37
+ logger.debug("overriding #{alarm.name} alarm properties for resource #{alarm.resource_id} in resource group #{group} via group overrides")
38
+ group_overrides.each {|attr,value| update_object(alarm,attr,value)}
36
39
  end
37
40
  end
38
41
 
39
42
  # loop over each override template for the service
40
- overides.each do |name,properties|
41
-
43
+ overides.each do |name,properties|
42
44
  # disable default alarms
43
45
  if [false].include?(properties)
44
- alarm = find_alarm(name)
46
+ alarms = find_alarms(name)
45
47
 
46
- if !alarm.nil?
47
- alarm.enabled = false
48
- logger.debug "Disabling alarm '#{name}' for resource #{alarm.resource_id}"
48
+ if !alarms.nil?
49
+ alarms.each do |alarm|
50
+ alarm.enabled = false
51
+ logger.info "disabling alarm '#{name}' for resource #{alarm.resource_id}"
52
+ end
49
53
  next
50
54
  end
51
55
  end
@@ -53,7 +57,7 @@ module CfnGuardian::Resource
53
57
  # continue if the override is in the incorrect format
54
58
  unless properties.is_a?(Hash)
55
59
  if name != 'Inherit'
56
- logger.warn "Incorrect format for alarm '#{name}'. Should be of type 'Hash', instead got type '#{properties.group}'"
60
+ logger.warn "incorrect format for alarm '#{name}'. Should be of type 'Hash', instead got type '#{properties.group}'"
57
61
  end
58
62
  next
59
63
  end
@@ -64,31 +68,35 @@ module CfnGuardian::Resource
64
68
  if properties.has_key?('Inherit')
65
69
  alarm = find_alarm(properties['Inherit'])
66
70
  if !alarm.nil?
71
+ logger.debug("creating new alarm #{name} for alarm group #{self.class.to_s.split('::').last} inheriting properties from alarm #{properties['Inherit']}")
67
72
  inheritited_alarm = alarm.clone
68
73
  alarm.name = name
69
- properties.each {|attr,value| update_alarm(inheritited_alarm,attr,value)}
74
+ properties.each {|attr,value| update_object(inheritited_alarm,attr,value)}
70
75
  @alarms.push(inheritited_alarm)
71
76
  else
72
- logger.warn "Alarm '#{properties['Inherit']}' doesn't exists and cannot be inherited"
77
+ logger.warn "alarm '#{properties['Inherit']}' doesn't exists and cannot be inherited"
73
78
  end
74
79
  next
75
80
  end
76
81
 
77
- alarm = find_alarm(name)
82
+ alarms = find_alarms(name)
78
83
 
79
- if alarm.nil?
80
- if @resource.has_key?('Hosts')
81
- logger.warn("this resource doesn't support adding new alarms")
82
- next
84
+ if alarms.empty?
85
+ # if the alarm doesn't exist and it's not being inherited from another alarm create a new alarm
86
+ resources = @resource.has_key?('Hosts') ? @resource['Hosts'] : [@resource]
87
+ resources.each do |res|
88
+ alarm = Kernel.const_get("CfnGuardian::Models::#{self.class.to_s.split('::').last}Alarm").new(res)
89
+ properties.each {|attr,value| update_object(alarm,attr,value)}
90
+ alarm.name = name
91
+ logger.debug("created new alarm #{alarm.name} for resource #{alarm.resource_id} in resource group #{group}")
92
+ @alarms.push(alarm)
83
93
  end
84
- # if alarm doesn't exist create a new one
85
- alarm = Kernel.const_get("CfnGuardian::Models::#{self.class.to_s.split('::').last}Alarm").new(@resource)
86
- properties.each {|attr,value| update_alarm(alarm,attr,value)}
87
- alarm.name = name
88
- @alarms.push(alarm)
89
94
  else
90
95
  # if there is an existing alarm update the properties
91
- properties.each {|attr,value| update_alarm(alarm,attr,value)}
96
+ alarms.each do |alarm|
97
+ logger.debug("overriding #{alarm.name} alarm properties for resource #{alarm.resource_id} in resource group #{group} via alarm overrides")
98
+ properties.each {|attr,value| update_object(alarm,attr,value)}
99
+ end
92
100
  end
93
101
  end
94
102
 
@@ -96,6 +104,20 @@ module CfnGuardian::Resource
96
104
  @alarms.each {|a| a.group = @override_group}
97
105
  end
98
106
 
107
+ # String interpolation for alarm dimensions
108
+ @alarms.each do |alarm|
109
+ next if alarm.dimensions.nil?
110
+ alarm.dimensions.each do |k,v|
111
+ if v.match?(/^\${Resource::.*[A-Za-z]}$/)
112
+ resource_key = v.tr('${}', '').split('Resource::').last
113
+ if @resource.has_key?(resource_key)
114
+ logger.debug "overriding alarm #{alarm.name} dimension key '#{k}' with value '#{@resource[resource_key]}'"
115
+ alarm.dimensions[k] = @resource[resource_key]
116
+ end
117
+ end
118
+ end
119
+ end
120
+
99
121
  return @alarms.select{|a| a.enabled}
100
122
  end
101
123
 
@@ -128,6 +150,60 @@ module CfnGuardian::Resource
128
150
  default_metric_filters()
129
151
  return @metric_filters
130
152
  end
153
+
154
+ # Overidden by inheritted classes to define default checks
155
+ def default_event_subscriptions()
156
+ return @event_subscriptions
157
+ end
158
+
159
+ def get_event_subscriptions(group, overides)
160
+ # generate defailt event subscriptions
161
+ default_event_subscriptions()
162
+
163
+ # overide the defaults
164
+ overides.each do |name, properties|
165
+ event_subscription = find_event_subscriptions(name)
166
+
167
+ # disbable the event subscription if the value is false
168
+ if [false].include?(properties)
169
+ unless event_subscription.nil?
170
+ event_subscription.enabled = false
171
+ logger.info "Disabling event subscription #{name} for #{group} #{event_subscription.resource_id}"
172
+ end
173
+
174
+ next
175
+ end
176
+
177
+ # ignore all properties not in a proper format
178
+ next unless properties.is_a?(Hash)
179
+
180
+ # Create a new event subscription by inheriting an existing one
181
+ if properties.has_key?('Inherit')
182
+ inherit_event_subscription = find_event_subscriptions(properties['Inherit'])
183
+
184
+ if inherit_event_subscription.nil?
185
+ logger.warn "Unable to create #{topic} RDSEventSubscription by inheriting #{properties['Inherit']} as it cannot be found"
186
+ next
187
+ end
188
+
189
+ event_subscription = inherit_event_subscription.clone
190
+ event_subscription.enabled = true
191
+ event_subscription.name = name
192
+ @event_subscriptions.push(event_subscription)
193
+ logger.debug "Inheriting RDSEventSubscription #{properties['Inherit']}"
194
+ end
195
+
196
+ if event_subscription.nil?
197
+ event_subscription = Kernel.const_get("CfnGuardian::Models::#{self.class.to_s.split('::').last}EventSubscription").new(@resource)
198
+ event_subscription.name = name
199
+ @event_subscriptions.push(event_subscription)
200
+ end
201
+
202
+ properties.each {|attr,value| update_object(event_subscription,attr,value)}
203
+ end
204
+
205
+ return @event_subscriptions.select {|es| es.enabled }
206
+ end
131
207
 
132
208
  def get_cost()
133
209
  return @alarms.length * 0.10
@@ -138,13 +214,22 @@ module CfnGuardian::Resource
138
214
  def find_alarm(name)
139
215
  @alarms.detect {|alarm| alarm.name == name}
140
216
  end
141
-
142
- def update_alarm(alarm,attr,value)
217
+
218
+ def find_alarms(name)
219
+ @alarms.find_all {|alarm| alarm.name == name}
220
+ end
221
+
222
+ def find_event_subscriptions(name)
223
+ @event_subscriptions.detect {|es| es.name == name}
224
+ end
225
+
226
+ def update_object(obj,attr,value)
227
+ logger.debug("overriding #{obj.type} property '#{attr}' with value #{value} for resource id: #{obj.resource_id}")
143
228
  begin
144
- alarm.send("#{attr.to_underscore}=",value)
229
+ obj.send("#{attr.to_underscore}=",value.clone)
145
230
  rescue NoMethodError => e
146
231
  if !e.message.match?(/inherit/)
147
- logger.warn "Unknown key '#{attr}' for #{alarm.resource_id} alarm #{alarm.name}"
232
+ logger.warn "Unknown property '#{attr}' for type: #{obj.type} and resource id: #{obj.resource_id}"
148
233
  end
149
234
  end
150
235
  end