cfn-guardian 0.1.0 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +1 -0
  3. data/.github/workflows/build-gem.yml +25 -0
  4. data/.github/workflows/release-gem.yml +25 -0
  5. data/.github/workflows/release-image.yml +33 -0
  6. data/.rspec +1 -0
  7. data/Dockerfile +19 -0
  8. data/Gemfile.lock +39 -21
  9. data/README.md +9 -378
  10. data/cfn-guardian.gemspec +7 -5
  11. data/docs/alarm_templates.md +130 -0
  12. data/docs/cli.md +182 -0
  13. data/docs/composite_alarms.md +24 -0
  14. data/docs/custom_checks/azure_file_check.md +28 -0
  15. data/docs/custom_checks/domain_expiry.md +10 -0
  16. data/docs/custom_checks/http.md +59 -0
  17. data/docs/custom_checks/log_group_metric_filters.md +27 -0
  18. data/docs/custom_checks/nrpe.md +29 -0
  19. data/docs/custom_checks/port.md +40 -0
  20. data/docs/custom_checks/sftp.md +73 -0
  21. data/docs/custom_checks/sql.md +44 -0
  22. data/docs/custom_checks/tls.md +25 -0
  23. data/docs/custom_metrics.md +71 -0
  24. data/docs/event_subscriptions.md +67 -0
  25. data/docs/maintenance_mode.md +85 -0
  26. data/docs/notifiers.md +33 -0
  27. data/docs/overview.md +22 -0
  28. data/docs/resources.md +93 -0
  29. data/docs/variables.md +58 -0
  30. data/lib/cfnguardian.rb +325 -37
  31. data/lib/cfnguardian/cloudwatch.rb +132 -0
  32. data/lib/cfnguardian/codecommit.rb +54 -0
  33. data/lib/cfnguardian/codepipeline.rb +138 -0
  34. data/lib/cfnguardian/compile.rb +142 -18
  35. data/lib/cfnguardian/config/defaults.yaml +103 -0
  36. data/lib/cfnguardian/deploy.rb +2 -16
  37. data/lib/cfnguardian/display_formatter.rb +163 -0
  38. data/lib/cfnguardian/drift.rb +79 -0
  39. data/lib/cfnguardian/error.rb +4 -0
  40. data/lib/cfnguardian/log.rb +0 -1
  41. data/lib/cfnguardian/models/alarm.rb +193 -59
  42. data/lib/cfnguardian/models/check.rb +128 -33
  43. data/lib/cfnguardian/models/composite.rb +21 -0
  44. data/lib/cfnguardian/models/event.rb +201 -49
  45. data/lib/cfnguardian/models/event_subscription.rb +96 -0
  46. data/lib/cfnguardian/models/metric_filter.rb +28 -0
  47. data/lib/cfnguardian/resources/amazonmq_rabbitmq.rb +136 -0
  48. data/lib/cfnguardian/resources/application_targetgroup.rb +2 -0
  49. data/lib/cfnguardian/resources/azure_file.rb +20 -0
  50. data/lib/cfnguardian/resources/base.rb +155 -33
  51. data/lib/cfnguardian/resources/ec2_instance.rb +11 -0
  52. data/lib/cfnguardian/resources/ecs_service.rb +2 -2
  53. data/lib/cfnguardian/resources/http.rb +17 -1
  54. data/lib/cfnguardian/resources/internal_http.rb +74 -0
  55. data/lib/cfnguardian/resources/internal_port.rb +33 -0
  56. data/lib/cfnguardian/resources/internal_sftp.rb +58 -0
  57. data/lib/cfnguardian/resources/log_group.rb +26 -0
  58. data/lib/cfnguardian/resources/network_targetgroup.rb +1 -0
  59. data/lib/cfnguardian/resources/port.rb +25 -0
  60. data/lib/cfnguardian/resources/rds_cluster.rb +14 -0
  61. data/lib/cfnguardian/resources/rds_instance.rb +73 -0
  62. data/lib/cfnguardian/resources/redshift_cluster.rb +2 -2
  63. data/lib/cfnguardian/resources/sftp.rb +50 -0
  64. data/lib/cfnguardian/resources/sql.rb +3 -3
  65. data/lib/cfnguardian/resources/tls.rb +66 -0
  66. data/lib/cfnguardian/s3.rb +3 -2
  67. data/lib/cfnguardian/stacks/main.rb +94 -72
  68. data/lib/cfnguardian/stacks/resources.rb +111 -43
  69. data/lib/cfnguardian/string.rb +12 -0
  70. data/lib/cfnguardian/version.rb +1 -1
  71. metadata +133 -10
@@ -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,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
@@ -0,0 +1,136 @@
1
+ module CfnGuardian::Resource
2
+ class AmazonMQRabbitMQBroker < Base
3
+
4
+ def default_alarms
5
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQBrokerAlarm.new(@resource)
6
+ alarm.name = 'ConnectionCountCritical'
7
+ alarm.metric_name = 'ConnectionCount'
8
+ alarm.comparison_operator = 'GreaterThanThreshold'
9
+ alarm.statistic = 'Maximum'
10
+ alarm.threshold = 50
11
+ alarm.evaluation_periods = 5
12
+ alarm.treat_missing_data = 'notBreaching'
13
+ @alarms.push(alarm)
14
+
15
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQBrokerAlarm.new(@resource)
16
+ alarm.name = 'ConnectionCountWarn'
17
+ alarm.metric_name = 'ConnectionCount'
18
+ alarm.comparison_operator = 'GreaterThanThreshold'
19
+ alarm.statistic = 'Maximum'
20
+ alarm.threshold = 25
21
+ alarm.evaluation_periods = 5
22
+ alarm.treat_missing_data = 'notBreaching'
23
+ alarm.alarm_action = 'Warning'
24
+ @alarms.push(alarm)
25
+
26
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQBrokerAlarm.new(@resource)
27
+ alarm.name = 'MessageCountCritical'
28
+ alarm.metric_name = 'MessageCount'
29
+ alarm.comparison_operator = 'GreaterThanThreshold'
30
+ alarm.statistic = 'Maximum'
31
+ alarm.threshold = 500
32
+ alarm.evaluation_periods = 5
33
+ alarm.treat_missing_data = 'notBreaching'
34
+ @alarms.push(alarm)
35
+
36
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQBrokerAlarm.new(@resource)
37
+ alarm.name = 'MessageCountWarn'
38
+ alarm.metric_name = 'MessageCount'
39
+ alarm.comparison_operator = 'GreaterThanThreshold'
40
+ alarm.statistic = 'Maximum'
41
+ alarm.threshold = 250
42
+ alarm.evaluation_periods = 5
43
+ alarm.treat_missing_data = 'notBreaching'
44
+ alarm.alarm_action = 'Warning'
45
+ @alarms.push(alarm)
46
+
47
+ end
48
+ end
49
+
50
+ class AmazonMQRabbitMQQueue < Base
51
+
52
+ def default_alarms
53
+
54
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQQueueAlarm.new(@resource)
55
+ alarm.name = 'MessageCountHighWarn'
56
+ alarm.metric_name = 'MessageCount'
57
+ alarm.comparison_operator = 'GreaterThanThreshold'
58
+ alarm.statistic = 'Maximum'
59
+ alarm.threshold = 100
60
+ alarm.evaluation_periods = 5
61
+ alarm.treat_missing_data = 'notBreaching'
62
+ alarm.alarm_action = 'Warning'
63
+ @alarms.push(alarm)
64
+
65
+ end
66
+ end
67
+
68
+ class AmazonMQRabbitMQNode < Base
69
+
70
+ def default_alarms
71
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQNodeAlarm.new(@resource)
72
+ alarm.name = 'SystemCpuUtilizationCritical'
73
+ alarm.metric_name = 'SystemCpuUtilization'
74
+ alarm.comparison_operator = 'GreaterThanThreshold'
75
+ alarm.statistic = 'Maximum'
76
+ alarm.threshold = 95
77
+ alarm.evaluation_periods = 10
78
+ alarm.treat_missing_data = 'notBreaching'
79
+ @alarms.push(alarm)
80
+
81
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQNodeAlarm.new(@resource)
82
+ alarm.name = 'SystemCpuUtilizationHighBase'
83
+ alarm.metric_name = 'SystemCpuUtilization'
84
+ alarm.comparison_operator = 'GreaterThanThreshold'
85
+ alarm.statistic = 'Maximum'
86
+ alarm.threshold = 75
87
+ alarm.evaluation_periods = 30
88
+ alarm.treat_missing_data = 'notBreaching'
89
+ alarm.alarm_action = 'Warning'
90
+ @alarms.push(alarm)
91
+
92
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQNodeAlarm.new(@resource)
93
+ alarm.name = 'RabbitMQMemUsedCritical'
94
+ alarm.metric_name = 'RabbitMQMemUsed'
95
+ alarm.comparison_operator = 'GreaterThanThreshold'
96
+ alarm.statistic = 'Maximum'
97
+ alarm.threshold = 390000000
98
+ alarm.evaluation_periods = 5
99
+ alarm.treat_missing_data = 'notBreaching'
100
+ @alarms.push(alarm)
101
+
102
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQNodeAlarm.new(@resource)
103
+ alarm.name = 'RabbitMQMemUsedWarn'
104
+ alarm.metric_name = 'RabbitMQMemUsed'
105
+ alarm.comparison_operator = 'GreaterThanThreshold'
106
+ alarm.statistic = 'Maximum'
107
+ alarm.threshold = 350000000
108
+ alarm.evaluation_periods = 5
109
+ alarm.treat_missing_data = 'notBreaching'
110
+ alarm.alarm_action = 'Warning'
111
+ @alarms.push(alarm)
112
+
113
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQNodeAlarm.new(@resource)
114
+ alarm.name = 'RabbitMQDiskFreeLimitCritical'
115
+ alarm.metric_name = 'RabbitMQDiskFreeLimit'
116
+ alarm.comparison_operator = 'LessThanThreshold'
117
+ alarm.statistic = 'Maximum'
118
+ alarm.threshold = 1200000000
119
+ alarm.evaluation_periods = 5
120
+ alarm.treat_missing_data = 'notBreaching'
121
+ @alarms.push(alarm)
122
+
123
+ alarm = CfnGuardian::Models::AmazonMQRabbitMQNodeAlarm.new(@resource)
124
+ alarm.name = 'RabbitMQDiskFreeLimitWarn'
125
+ alarm.metric_name = 'RabbitMQDiskFreeLimit'
126
+ alarm.comparison_operator = 'LessThanThreshold'
127
+ alarm.statistic = 'Maximum'
128
+ alarm.threshold = 1200000000
129
+ alarm.evaluation_periods = 5
130
+ alarm.treat_missing_data = 'notBreaching'
131
+ alarm.alarm_action = 'Warning'
132
+ @alarms.push(alarm)
133
+
134
+ end
135
+ end
136
+ 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)
@@ -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
@@ -1,95 +1,208 @@
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'
7
+ require 'cfnguardian/models/event_subscription'
5
8
 
6
9
  module CfnGuardian::Resource
7
10
  class Base
8
11
  include Logging
9
12
 
10
- def initialize(resource)
13
+ def initialize(resource, override_group = nil)
11
14
  @resource = resource
15
+ @override_group = override_group
12
16
  @alarms = []
13
17
  @events = []
14
18
  @checks = []
19
+ @metric_filters = []
20
+ @event_subscriptions = []
15
21
  end
16
22
 
23
+ # Overidden by inheritted classes to define default alarms
17
24
  def default_alarms()
18
25
  return @alarms
19
26
  end
20
27
 
21
- def get_alarms(overides={})
28
+ def get_alarms(group,overides={})
22
29
  # generate default alarms
23
30
  default_alarms()
24
-
31
+
32
+ # override any group properties
33
+ group_overrides = overides.has_key?('GroupOverrides') ? overides['GroupOverrides'] : {}
34
+ overides.delete('GroupOverrides')
35
+ if group_overrides.any?
36
+ @alarms.each do |alarm|
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)}
39
+ end
40
+ end
41
+
25
42
  # loop over each override template for the service
26
- overides.each do |name,properties|
27
-
43
+ overides.each do |name,properties|
28
44
  # disable default alarms
29
45
  if [false].include?(properties)
30
- alarm = find_alarm(name)
46
+ alarms = find_alarms(name)
31
47
 
32
- if !alarm.nil?
33
- alarm.enabled = false
34
- logger.debug "Disabling alarm '#{name}' for resource #{alarm.resource}"
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
35
53
  next
36
54
  end
37
55
  end
38
-
56
+
57
+ # continue if the override is in the incorrect format
39
58
  unless properties.is_a?(Hash)
40
59
  if name != 'Inherit'
41
- logger.warn "Incorrect format for alarm '#{name}'. Should be of type 'Hash', instead got type '#{properties.class}'"
60
+ logger.warn "incorrect format for alarm '#{name}'. Should be of type 'Hash', instead got type '#{properties.group}'"
42
61
  end
43
62
  next
44
63
  end
45
-
64
+
65
+ properties.merge!(group_overrides)
66
+
67
+ # Create a new alarm inheriting the defaults of an existing alarm
46
68
  if properties.has_key?('Inherit')
47
69
  alarm = find_alarm(properties['Inherit'])
48
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']}")
49
72
  inheritited_alarm = alarm.clone
50
73
  alarm.name = name
51
- properties.each {|attr,value| update_alarm(inheritited_alarm,attr,value)}
74
+ properties.each {|attr,value| update_object(inheritited_alarm,attr,value)}
52
75
  @alarms.push(inheritited_alarm)
53
76
  else
54
- 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"
55
78
  end
56
79
  next
57
80
  end
58
81
 
59
- alarm = find_alarm(name)
60
-
61
- if alarm.nil?
62
- alarm = Kernel.const_get("CfnGuardian::Models::#{self.class.to_s.split('::').last}Alarm").new(properties)
63
- end
64
-
65
- if alarm.name.nil?
66
- alarm.name = name
82
+ alarms = find_alarms(name)
83
+
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)
93
+ end
94
+ else
95
+ # if there is an existing alarm update the properties
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
67
100
  end
68
-
69
- properties.each {|attr,value| update_alarm(alarm,attr,value)}
70
- @alarms.push(alarm)
71
-
72
101
  end
73
102
 
74
- return @alarms.select{|a| a.enabled}.map {|a| a.to_h}
103
+ unless @override_group.nil?
104
+ @alarms.each {|a| a.group = @override_group}
105
+ end
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
+
121
+ return @alarms.select{|a| a.enabled}
75
122
  end
76
123
 
124
+ # Overidden by inheritted classes to define default events
77
125
  def default_events()
78
126
  return @events
79
127
  end
80
128
 
81
129
  def get_events()
82
130
  default_events()
83
- return @events.select{|e| e.enabled}.map {|e| e.to_h}
131
+ return @events.select{|e| e.enabled}
84
132
  end
85
133
 
134
+ # Overidden by inheritted classes to define default checks
86
135
  def default_checks()
87
136
  return @checks
88
137
  end
89
138
 
90
139
  def get_checks()
91
140
  default_checks()
92
- return @checks.map {|c| c.to_h}
141
+ return @checks
142
+ end
143
+
144
+ # Overidden by inheritted classes to define default checks
145
+ def default_metric_filters()
146
+ return @metric_filters
147
+ end
148
+
149
+ def get_metric_filters()
150
+ default_metric_filters()
151
+ return @metric_filters
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 }
93
206
  end
94
207
 
95
208
  def get_cost()
@@ -101,13 +214,22 @@ module CfnGuardian::Resource
101
214
  def find_alarm(name)
102
215
  @alarms.detect {|alarm| alarm.name == name}
103
216
  end
104
-
105
- 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}")
106
228
  begin
107
- alarm.send("#{attr.to_underscore}=",value)
229
+ obj.send("#{attr.to_underscore}=",value.clone)
108
230
  rescue NoMethodError => e
109
231
  if !e.message.match?(/inherit/)
110
- logger.warn "Unknown key '#{attr}' for #{alarm.resource} alarm #{alarm.name}"
232
+ logger.warn "Unknown property '#{attr}' for type: #{obj.type} and resource id: #{obj.resource_id}"
111
233
  end
112
234
  end
113
235
  end