cfn-guardian 0.1.0 → 0.6.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.
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