cfn-guardian 0.1.0 → 0.3.3
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 +4 -4
- data/.dockerignore +1 -0
- data/Dockerfile +19 -0
- data/Gemfile.lock +31 -13
- data/README.md +441 -42
- data/cfn-guardian.gemspec +6 -2
- data/lib/cfnguardian.rb +301 -27
- data/lib/cfnguardian/cloudwatch.rb +121 -0
- data/lib/cfnguardian/codecommit.rb +54 -0
- data/lib/cfnguardian/codepipeline.rb +138 -0
- data/lib/cfnguardian/compile.rb +58 -17
- data/lib/cfnguardian/config/defaults.yaml +94 -0
- data/lib/cfnguardian/display_formatter.rb +164 -0
- data/lib/cfnguardian/drift.rb +79 -0
- data/lib/cfnguardian/log.rb +0 -1
- data/lib/cfnguardian/models/alarm.rb +98 -36
- data/lib/cfnguardian/models/check.rb +103 -26
- data/lib/cfnguardian/models/composite.rb +21 -0
- data/lib/cfnguardian/models/event.rb +164 -40
- data/lib/cfnguardian/models/metric_filter.rb +28 -0
- data/lib/cfnguardian/resources/application_targetgroup.rb +2 -0
- data/lib/cfnguardian/resources/base.rb +38 -16
- data/lib/cfnguardian/resources/ecs_service.rb +2 -2
- data/lib/cfnguardian/resources/http.rb +16 -1
- data/lib/cfnguardian/resources/internal_http.rb +74 -0
- data/lib/cfnguardian/resources/internal_port.rb +33 -0
- data/lib/cfnguardian/resources/internal_sftp.rb +58 -0
- data/lib/cfnguardian/resources/log_group.rb +26 -0
- data/lib/cfnguardian/resources/network_targetgroup.rb +1 -0
- data/lib/cfnguardian/resources/port.rb +25 -0
- data/lib/cfnguardian/resources/rds_instance.rb +2 -0
- data/lib/cfnguardian/resources/sftp.rb +50 -0
- data/lib/cfnguardian/resources/sql.rb +1 -1
- data/lib/cfnguardian/resources/tls.rb +66 -0
- data/lib/cfnguardian/s3.rb +3 -2
- data/lib/cfnguardian/stacks/main.rb +86 -65
- data/lib/cfnguardian/stacks/resources.rb +81 -42
- data/lib/cfnguardian/string.rb +12 -0
- data/lib/cfnguardian/version.rb +1 -1
- metadata +102 -5
@@ -0,0 +1,21 @@
|
|
1
|
+
module CfnGuardian
|
2
|
+
module Models
|
3
|
+
class Composite
|
4
|
+
|
5
|
+
attr_reader :type
|
6
|
+
attr_accessor :name,
|
7
|
+
:description,
|
8
|
+
:rule,
|
9
|
+
:alarm_action
|
10
|
+
|
11
|
+
def initialize(name,params = {})
|
12
|
+
@type = 'Composite'
|
13
|
+
@name = name
|
14
|
+
@description = params.fetch('Description', '')
|
15
|
+
@rule = params.fetch('Rule', 'FALSE')
|
16
|
+
@alarm_action = params.fetch('Action', nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -5,43 +5,30 @@ module CfnGuardian
|
|
5
5
|
class Event
|
6
6
|
|
7
7
|
attr_reader :type
|
8
|
-
attr_accessor :
|
8
|
+
attr_accessor :group,
|
9
9
|
:target,
|
10
10
|
:hash,
|
11
11
|
:name,
|
12
12
|
:cron,
|
13
13
|
:enabled,
|
14
|
-
:resource
|
14
|
+
:resource,
|
15
|
+
:environment,
|
16
|
+
:payload,
|
17
|
+
:ssm_parameters
|
15
18
|
|
16
19
|
def initialize(resource)
|
17
20
|
@type = 'Event'
|
18
|
-
@
|
21
|
+
@group = nil
|
19
22
|
@target = nil
|
20
23
|
@hash = Digest::MD5.hexdigest resource['Id']
|
21
24
|
@name = @hash
|
22
25
|
@cron = "* * * * ? *"
|
23
26
|
@enabled = true
|
24
27
|
@resource = resource['Id'].to_resource_name
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
type: @type,
|
30
|
-
class: @class,
|
31
|
-
target: @target,
|
32
|
-
hash: @hash,
|
33
|
-
name: @name,
|
34
|
-
cron: @cron,
|
35
|
-
enabled: @enabled,
|
36
|
-
resource: @resource,
|
37
|
-
payload: event_payload()
|
38
|
-
}
|
39
|
-
end
|
40
|
-
|
41
|
-
def event_payload
|
42
|
-
{}.to_json
|
43
|
-
end
|
44
|
-
|
28
|
+
@environment = ""
|
29
|
+
@payload = {}.to_json
|
30
|
+
@ssm_parameters = []
|
31
|
+
end
|
45
32
|
end
|
46
33
|
|
47
34
|
class HttpEvent < Event
|
@@ -56,7 +43,7 @@ module CfnGuardian
|
|
56
43
|
|
57
44
|
def initialize(resource)
|
58
45
|
super(resource)
|
59
|
-
@
|
46
|
+
@group = 'Http'
|
60
47
|
@name = 'HttpEvent'
|
61
48
|
@target = 'HttpCheckFunction'
|
62
49
|
@endpoint = resource['Id']
|
@@ -66,9 +53,10 @@ module CfnGuardian
|
|
66
53
|
@body_regex = resource.fetch('BodyRegex',nil)
|
67
54
|
@headers = resource.fetch('Headers',nil)
|
68
55
|
@payload = resource.fetch('Payload',nil)
|
56
|
+
@compressed = resource.fetch('Compressed',false)
|
69
57
|
end
|
70
58
|
|
71
|
-
def
|
59
|
+
def payload
|
72
60
|
payload = {
|
73
61
|
'ENDPOINT' => @endpoint,
|
74
62
|
'METHOD' => @method,
|
@@ -78,14 +66,56 @@ module CfnGuardian
|
|
78
66
|
payload['BODY_REGEX_MATCH'] = @body_regex unless @body_regex.nil?
|
79
67
|
payload['HEADERS'] = @headers unless @headers.nil?
|
80
68
|
payload['PAYLOAD'] = @payload unless @payload.nil?
|
69
|
+
payload['COMPRESSED'] = '1' if @compressed
|
81
70
|
return payload.to_json
|
82
71
|
end
|
83
72
|
end
|
84
73
|
|
85
|
-
class
|
74
|
+
class InternalHttpEvent < HttpEvent
|
75
|
+
def initialize(resource,environment)
|
76
|
+
super(resource)
|
77
|
+
@group = 'InternalHttp'
|
78
|
+
@name = 'InternalHttpEvent'
|
79
|
+
@target = "InternalHttpCheckFunction#{environment}"
|
80
|
+
@environment = environment
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class PortEvent < Event
|
85
|
+
def initialize(resource)
|
86
|
+
super(resource)
|
87
|
+
@group = 'Port'
|
88
|
+
@name = 'PortEvent'
|
89
|
+
@target = 'PortCheckFunction'
|
90
|
+
@hostname = resource['Id']
|
91
|
+
@port = resource['Port']
|
92
|
+
@timeout = resource.fetch('Timeout',120)
|
93
|
+
end
|
94
|
+
|
95
|
+
def payload
|
96
|
+
return {
|
97
|
+
'HOSTNAME' => @hostname,
|
98
|
+
'PORT' => @port,
|
99
|
+
'TIMEOUT' => @timeout,
|
100
|
+
'STATUS_CODE_MATCH' => @status_code
|
101
|
+
}.to_json
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class InternalPortEvent < PortEvent
|
106
|
+
def initialize(resource,environment)
|
107
|
+
super(resource)
|
108
|
+
@group = 'InternalPort'
|
109
|
+
@name = 'InternalPortEvent'
|
110
|
+
@target = "InternalPortCheckFunction#{environment}"
|
111
|
+
@environment = environment
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class NrpeEvent < Event
|
86
116
|
def initialize(resource,environment,command)
|
87
117
|
super(resource)
|
88
|
-
@
|
118
|
+
@group = 'Nrpe'
|
89
119
|
@name = 'NrpeEvent'
|
90
120
|
@target = "NrpeCheckFunction#{environment}"
|
91
121
|
@host = resource['Id']
|
@@ -94,7 +124,7 @@ module CfnGuardian
|
|
94
124
|
@command = command
|
95
125
|
end
|
96
126
|
|
97
|
-
def
|
127
|
+
def payload
|
98
128
|
return {
|
99
129
|
'host' => @host,
|
100
130
|
'environment' => @environment,
|
@@ -107,7 +137,7 @@ module CfnGuardian
|
|
107
137
|
class SslEvent < Event
|
108
138
|
def initialize(resource)
|
109
139
|
super(resource)
|
110
|
-
@
|
140
|
+
@group = 'Ssl'
|
111
141
|
@name = 'SslEvent'
|
112
142
|
@target = 'SslCheckFunction'
|
113
143
|
@cron = "0 12 * * ? *"
|
@@ -115,7 +145,7 @@ module CfnGuardian
|
|
115
145
|
@region = resource.fetch('Region',"${AWS::Region}")
|
116
146
|
end
|
117
147
|
|
118
|
-
def
|
148
|
+
def payload
|
119
149
|
return {
|
120
150
|
'Url' => @url,
|
121
151
|
'Region' => @region
|
@@ -123,6 +153,16 @@ module CfnGuardian
|
|
123
153
|
end
|
124
154
|
end
|
125
155
|
|
156
|
+
class InternalSslEvent < SslEvent
|
157
|
+
def initialize(resource,environment)
|
158
|
+
super(resource)
|
159
|
+
@group = 'InternalSsl'
|
160
|
+
@name = 'InternalSslEvent'
|
161
|
+
@target = "InternalSslCheckFunction#{environment}"
|
162
|
+
@environment = environment
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
126
166
|
class DomainExpiryEvent < Event
|
127
167
|
|
128
168
|
attr_accessor :domain,
|
@@ -130,7 +170,7 @@ module CfnGuardian
|
|
130
170
|
|
131
171
|
def initialize(resource)
|
132
172
|
super(resource)
|
133
|
-
@
|
173
|
+
@group = 'DomainExpiry'
|
134
174
|
@name = 'DomainExpiryEvent'
|
135
175
|
@target = 'DomainExpiryCheckFunction'
|
136
176
|
@cron = "0 12 * * ? *"
|
@@ -138,17 +178,17 @@ module CfnGuardian
|
|
138
178
|
@region = resource.fetch('Region',"${AWS::Region}")
|
139
179
|
end
|
140
180
|
|
141
|
-
def
|
142
|
-
{'Domain' => @domain}.to_json
|
181
|
+
def payload
|
182
|
+
return {'Domain' => @domain}.to_json
|
143
183
|
end
|
144
184
|
end
|
145
185
|
|
146
186
|
class SqlEvent < Event
|
147
|
-
def initialize(resource,query)
|
187
|
+
def initialize(resource,query,environment)
|
148
188
|
super(resource)
|
149
|
-
@
|
189
|
+
@group = 'Sql'
|
150
190
|
@name = 'SqlEvent'
|
151
|
-
@target =
|
191
|
+
@target = "SqlCheckFunction#{environment}"
|
152
192
|
@host = resource['Id']
|
153
193
|
@engine = resource['Engine']
|
154
194
|
@port = resource['Port']
|
@@ -157,9 +197,10 @@ module CfnGuardian
|
|
157
197
|
@query = query
|
158
198
|
@region = resource.fetch('Region',"${AWS::Region}")
|
159
199
|
@test_type = '1-row-1-value-zero-is-good'
|
200
|
+
@environment = environment
|
160
201
|
end
|
161
202
|
|
162
|
-
def
|
203
|
+
def payload
|
163
204
|
return {
|
164
205
|
'Host' => @host,
|
165
206
|
'Engine' => @engine,
|
@@ -171,20 +212,103 @@ module CfnGuardian
|
|
171
212
|
'TestType' => @test_type
|
172
213
|
}.to_json
|
173
214
|
end
|
215
|
+
|
216
|
+
def ssm_parameters
|
217
|
+
params = []
|
218
|
+
params << @ssm_username
|
219
|
+
params << @ssm_password
|
220
|
+
return params
|
221
|
+
end
|
174
222
|
end
|
175
223
|
|
176
224
|
class ContainerInstanceEvent < Event
|
177
225
|
def initialize(resource)
|
178
226
|
super(resource)
|
179
|
-
@
|
227
|
+
@group = 'ContainerInstance'
|
180
228
|
@name = 'ContainerInstanceEvent'
|
181
229
|
@target = 'ContainerInstanceCheckFunction'
|
182
230
|
@cron = "0/5 * * * ? *"
|
183
231
|
@cluster = resource['Id']
|
184
232
|
end
|
185
233
|
|
186
|
-
def
|
187
|
-
{'CLUSTER' => @cluster}.to_json
|
234
|
+
def payload
|
235
|
+
return {'CLUSTER' => @cluster}.to_json
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class SFTPEvent < Event
|
240
|
+
def initialize(resource)
|
241
|
+
super(resource)
|
242
|
+
@group = 'SFTP'
|
243
|
+
@name = 'SFTPEvent'
|
244
|
+
@target = 'SFTPCheckFunction'
|
245
|
+
@cron = "0/5 * * * ? *"
|
246
|
+
@host = resource['Id']
|
247
|
+
@user = resource['User']
|
248
|
+
@port = resource.fetch('Port', nil)
|
249
|
+
@server_key = resource.fetch('ServerKey', nil)
|
250
|
+
@password = resource.fetch('Password', nil)
|
251
|
+
@private_key = resource.fetch('PrivateKey', nil)
|
252
|
+
@private_key_pass = resource.fetch('PrivateKeyPass', nil)
|
253
|
+
@file = resource.fetch('File', nil)
|
254
|
+
@file_regex_match = resource.fetch('FileRegexMatch', nil)
|
255
|
+
end
|
256
|
+
|
257
|
+
def payload
|
258
|
+
payload = {
|
259
|
+
'HOSTNAME' => @host,
|
260
|
+
'USERNAME' => @user
|
261
|
+
}
|
262
|
+
payload['PORT'] = @port unless @port.nil?
|
263
|
+
payload['SERVER_KEY'] = @server_key unless @server_key.nil?
|
264
|
+
payload['PASSWORD'] = @password unless @password.nil?
|
265
|
+
payload['PRIVATEKEY'] = @private_key unless @private_key.nil?
|
266
|
+
payload['PRIVATEKEY_PASSWORD'] = @private_key_pass unless @private_key_pass.nil?
|
267
|
+
payload['FILE'] = @file unless @file.nil?
|
268
|
+
payload['FILE_REGEX_MATCH'] = @file_regex_match unless @file_regex_match.nil?
|
269
|
+
return payload.to_json
|
270
|
+
end
|
271
|
+
|
272
|
+
def ssm_parameters
|
273
|
+
params = []
|
274
|
+
params << @password unless @password.nil?
|
275
|
+
params << @private_key unless @private_key.nil?
|
276
|
+
params << @private_key_pass unless @private_key_pass.nil?
|
277
|
+
return params
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
class InternalSFTPEvent < SFTPEvent
|
282
|
+
def initialize(resource,environment)
|
283
|
+
super(resource)
|
284
|
+
@group = 'InternalSFTP'
|
285
|
+
@name = 'InternalSFTPEvent'
|
286
|
+
@target = "InternalSFTPCheckFunction#{environment}"
|
287
|
+
@environment = environment
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
class TLSEvent < Event
|
292
|
+
def initialize(resource)
|
293
|
+
super(resource)
|
294
|
+
@group = 'TLS'
|
295
|
+
@name = 'TLSEvent'
|
296
|
+
@target = 'TLSCheckFunction'
|
297
|
+
@cron = "0/5 * * * ? *"
|
298
|
+
@host = resource['Id']
|
299
|
+
@port = resource.fetch('Port', 443)
|
300
|
+
@check_max = resource.fetch('MaxSupported', nil)
|
301
|
+
@versions = resource.fetch('Versions', ['SSLv2','SSLv3','TLSv1','TLSv1.1','TLSv1.2'])
|
302
|
+
end
|
303
|
+
|
304
|
+
def payload
|
305
|
+
payload = {
|
306
|
+
'HOSTNAME' => @host,
|
307
|
+
'PORT' => @port
|
308
|
+
}
|
309
|
+
payload['CHECK_MAX_SUPPORTED'] = @check_max.nil?
|
310
|
+
payload['PROTOCOLS'] = @versions unless @versions.nil?
|
311
|
+
return payload.to_json
|
188
312
|
end
|
189
313
|
end
|
190
314
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'cfnguardian/string'
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
module CfnGuardian
|
5
|
+
module Models
|
6
|
+
class MetricFilter
|
7
|
+
|
8
|
+
attr_reader :type,
|
9
|
+
:metric_namespace,
|
10
|
+
:name
|
11
|
+
attr_accessor :log_group,
|
12
|
+
:pattern,
|
13
|
+
:metric_value,
|
14
|
+
:metric_name
|
15
|
+
|
16
|
+
def initialize(log_group,filter)
|
17
|
+
@type = 'MetricFilter'
|
18
|
+
@name = Digest::MD5.hexdigest(log_group + filter['MetricName'])
|
19
|
+
@log_group = log_group
|
20
|
+
@pattern = filter['Pattern']
|
21
|
+
@metric_value = filter.fetch('MetricValue', '1').to_s
|
22
|
+
@metric_name = filter['MetricName']
|
23
|
+
@metric_namespace = "MetricFilters"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -5,9 +5,11 @@ module CfnGuardian::Resource
|
|
5
5
|
alarm = CfnGuardian::Models::ApplicationTargetGroupAlarm.new(@resource)
|
6
6
|
alarm.name = 'HealthyHosts'
|
7
7
|
alarm.metric_name = 'HealthyHostCount'
|
8
|
+
alarm.comparison_operator = 'LessThanThreshold'
|
8
9
|
alarm.statistic = 'Minimum'
|
9
10
|
alarm.threshold = 2
|
10
11
|
alarm.evaluation_periods = 1
|
12
|
+
alarm.comparison_operator = 'LessThanThreshold'
|
11
13
|
@alarms.push(alarm)
|
12
14
|
|
13
15
|
alarm = CfnGuardian::Models::ApplicationTargetGroupAlarm.new(@resource)
|
@@ -1,24 +1,29 @@
|
|
1
1
|
require 'cfnguardian/string'
|
2
|
+
require 'cfnguardian/cloudwatch'
|
2
3
|
require 'cfnguardian/models/alarm'
|
3
4
|
require 'cfnguardian/models/event'
|
4
5
|
require 'cfnguardian/models/check'
|
6
|
+
require 'cfnguardian/models/metric_filter'
|
5
7
|
|
6
8
|
module CfnGuardian::Resource
|
7
9
|
class Base
|
8
10
|
include Logging
|
9
11
|
|
10
|
-
def initialize(resource)
|
12
|
+
def initialize(resource, override_group = nil)
|
11
13
|
@resource = resource
|
14
|
+
@override_group = override_group
|
12
15
|
@alarms = []
|
13
16
|
@events = []
|
14
17
|
@checks = []
|
18
|
+
@metric_filters = []
|
15
19
|
end
|
16
20
|
|
21
|
+
# Overidden by inheritted classes to define default alarms
|
17
22
|
def default_alarms()
|
18
23
|
return @alarms
|
19
24
|
end
|
20
25
|
|
21
|
-
def get_alarms(overides={})
|
26
|
+
def get_alarms(overides={},resource={})
|
22
27
|
# generate default alarms
|
23
28
|
default_alarms()
|
24
29
|
|
@@ -31,18 +36,20 @@ module CfnGuardian::Resource
|
|
31
36
|
|
32
37
|
if !alarm.nil?
|
33
38
|
alarm.enabled = false
|
34
|
-
logger.debug "Disabling alarm '#{name}' for resource #{alarm.
|
39
|
+
logger.debug "Disabling alarm '#{name}' for resource #{alarm.resource_id}"
|
35
40
|
next
|
36
41
|
end
|
37
42
|
end
|
38
43
|
|
44
|
+
# continue if the override is in the incorrect format
|
39
45
|
unless properties.is_a?(Hash)
|
40
46
|
if name != 'Inherit'
|
41
|
-
logger.warn "Incorrect format for alarm '#{name}'. Should be of type 'Hash', instead got type '#{properties.
|
47
|
+
logger.warn "Incorrect format for alarm '#{name}'. Should be of type 'Hash', instead got type '#{properties.group}'"
|
42
48
|
end
|
43
49
|
next
|
44
50
|
end
|
45
51
|
|
52
|
+
# Create a new alarm inheriting the defaults of an existing alarm
|
46
53
|
if properties.has_key?('Inherit')
|
47
54
|
alarm = find_alarm(properties['Inherit'])
|
48
55
|
if !alarm.nil?
|
@@ -59,37 +66,52 @@ module CfnGuardian::Resource
|
|
59
66
|
alarm = find_alarm(name)
|
60
67
|
|
61
68
|
if alarm.nil?
|
62
|
-
alarm
|
63
|
-
|
64
|
-
|
65
|
-
if alarm.name.nil?
|
69
|
+
# if alarm doesn't exist create a new one
|
70
|
+
alarm = Kernel.const_get("CfnGuardian::Models::#{self.class.to_s.split('::').last}Alarm").new(resource)
|
71
|
+
properties.each {|attr,value| update_alarm(alarm,attr,value)}
|
66
72
|
alarm.name = name
|
73
|
+
@alarms.push(alarm)
|
74
|
+
else
|
75
|
+
# if there is an existing alarm update the properties
|
76
|
+
properties.each {|attr,value| update_alarm(alarm,attr,value)}
|
67
77
|
end
|
68
|
-
|
69
|
-
properties.each {|attr,value| update_alarm(alarm,attr,value)}
|
70
|
-
@alarms.push(alarm)
|
71
|
-
|
72
78
|
end
|
73
79
|
|
74
|
-
|
80
|
+
unless @override_group.nil?
|
81
|
+
@alarms.each {|a| a.group = @override_group}
|
82
|
+
end
|
83
|
+
|
84
|
+
return @alarms.select{|a| a.enabled}
|
75
85
|
end
|
76
86
|
|
87
|
+
# Overidden by inheritted classes to define default events
|
77
88
|
def default_events()
|
78
89
|
return @events
|
79
90
|
end
|
80
91
|
|
81
92
|
def get_events()
|
82
93
|
default_events()
|
83
|
-
return @events.select{|e| e.enabled}
|
94
|
+
return @events.select{|e| e.enabled}
|
84
95
|
end
|
85
96
|
|
97
|
+
# Overidden by inheritted classes to define default checks
|
86
98
|
def default_checks()
|
87
99
|
return @checks
|
88
100
|
end
|
89
101
|
|
90
102
|
def get_checks()
|
91
103
|
default_checks()
|
92
|
-
return @checks
|
104
|
+
return @checks
|
105
|
+
end
|
106
|
+
|
107
|
+
# Overidden by inheritted classes to define default checks
|
108
|
+
def default_metric_filters()
|
109
|
+
return @metric_filters
|
110
|
+
end
|
111
|
+
|
112
|
+
def get_metric_filters()
|
113
|
+
default_metric_filters()
|
114
|
+
return @metric_filters
|
93
115
|
end
|
94
116
|
|
95
117
|
def get_cost()
|
@@ -107,7 +129,7 @@ module CfnGuardian::Resource
|
|
107
129
|
alarm.send("#{attr.to_underscore}=",value)
|
108
130
|
rescue NoMethodError => e
|
109
131
|
if !e.message.match?(/inherit/)
|
110
|
-
logger.warn "Unknown key '#{attr}' for #{alarm.
|
132
|
+
logger.warn "Unknown key '#{attr}' for #{alarm.resource_id} alarm #{alarm.name}"
|
111
133
|
end
|
112
134
|
end
|
113
135
|
end
|