cfn-guardian 0.4.0 → 0.6.2
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/.github/workflows/build-gem.yml +25 -0
- data/.github/workflows/release-gem.yml +25 -0
- data/.github/workflows/release-image.yml +33 -0
- data/.rspec +1 -0
- data/Gemfile.lock +13 -13
- data/README.md +3 -819
- data/cfn-guardian.gemspec +1 -3
- data/docs/alarm_templates.md +130 -0
- data/docs/cli.md +182 -0
- data/docs/composite_alarms.md +24 -0
- data/docs/custom_checks/azure_file_check.md +28 -0
- data/docs/custom_checks/domain_expiry.md +10 -0
- data/docs/custom_checks/http.md +59 -0
- data/docs/custom_checks/log_group_metric_filters.md +27 -0
- data/docs/custom_checks/nrpe.md +29 -0
- data/docs/custom_checks/port.md +40 -0
- data/docs/custom_checks/sftp.md +73 -0
- data/docs/custom_checks/sql.md +44 -0
- data/docs/custom_checks/tls.md +25 -0
- data/docs/custom_metrics.md +71 -0
- data/docs/event_subscriptions.md +67 -0
- data/docs/maintenance_mode.md +85 -0
- data/docs/notifiers.md +33 -0
- data/docs/overview.md +22 -0
- data/docs/resources.md +93 -0
- data/docs/variables.md +58 -0
- data/lib/cfnguardian.rb +72 -58
- data/lib/cfnguardian/cloudwatch.rb +43 -32
- data/lib/cfnguardian/compile.rb +82 -5
- data/lib/cfnguardian/deploy.rb +2 -16
- data/lib/cfnguardian/display_formatter.rb +1 -2
- data/lib/cfnguardian/error.rb +4 -0
- data/lib/cfnguardian/models/alarm.rb +40 -28
- data/lib/cfnguardian/models/check.rb +30 -12
- data/lib/cfnguardian/models/event.rb +43 -15
- data/lib/cfnguardian/models/event_subscription.rb +96 -0
- data/lib/cfnguardian/resources/azure_file.rb +20 -0
- data/lib/cfnguardian/resources/base.rb +111 -26
- data/lib/cfnguardian/resources/ec2_instance.rb +11 -0
- data/lib/cfnguardian/resources/http.rb +1 -0
- data/lib/cfnguardian/resources/rds_cluster.rb +14 -0
- data/lib/cfnguardian/resources/rds_instance.rb +71 -0
- data/lib/cfnguardian/stacks/main.rb +7 -6
- data/lib/cfnguardian/stacks/resources.rb +34 -5
- data/lib/cfnguardian/version.rb +1 -1
- metadata +35 -10
@@ -2,7 +2,7 @@ require 'cfnguardian/string'
|
|
2
2
|
|
3
3
|
module CfnGuardian
|
4
4
|
module Models
|
5
|
-
class
|
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 <
|
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 = '
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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 <
|
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(
|
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
|
-
|
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
|
-
|
46
|
+
alarms = find_alarms(name)
|
45
47
|
|
46
|
-
if !
|
47
|
-
|
48
|
-
|
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 "
|
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|
|
74
|
+
properties.each {|attr,value| update_object(inheritited_alarm,attr,value)}
|
70
75
|
@alarms.push(inheritited_alarm)
|
71
76
|
else
|
72
|
-
logger.warn "
|
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
|
-
|
82
|
+
alarms = find_alarms(name)
|
78
83
|
|
79
|
-
if
|
80
|
-
if
|
81
|
-
|
82
|
-
|
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
|
-
|
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
|
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
|
-
|
229
|
+
obj.send("#{attr.to_underscore}=",value.clone)
|
145
230
|
rescue NoMethodError => e
|
146
231
|
if !e.message.match?(/inherit/)
|
147
|
-
logger.warn "Unknown
|
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
|