Class: CfnGuardian::Compile

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/cfnguardian/compile.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

colors, included, logger, #logger, logger=

Constructor Details

#initialize(config_file, check_resources_exist) ⇒ Compile

Returns a new instance of Compile.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/cfnguardian/compile.rb', line 66

def initialize(config_file, check_resources_exist)
  config = YAML.load_file(config_file)
        
  @resource_groups = config.fetch('Resources',{})
  @composites = config.fetch('Composites',{})
  @templates = config.fetch('Templates',{})
  @topics = config.fetch('Topics',{})
  @maintenance_groups = config.fetch('MaintenanceGroups', {})
  @event_subscriptions = config.fetch('EventSubscriptions', {})
  @global_tags = config.fetch('GlobalTags', {})
  @check_resources_exist = check_resources_exist
  @errors = []
  
  # Make sure the default topics exist if they aren't supplied in the alarms.yaml
  %w(Critical Warning Task Informational Events).each do |topic|
    @topics[topic] = '' unless @topics.has_key?(topic)
  end

  @resources = []
  @stacks = []
  @checks = []
  @ssm_parameters = []
  
  @cost = 0
end

Instance Attribute Details

#costObject (readonly)

Returns the value of attribute cost.



64
65
66
# File 'lib/cfnguardian/compile.rb', line 64

def cost
  @cost
end

#global_tagsObject (readonly)

Returns the value of attribute global_tags.



64
65
66
# File 'lib/cfnguardian/compile.rb', line 64

def global_tags
  @global_tags
end

#resourcesObject (readonly)

Returns the value of attribute resources.



64
65
66
# File 'lib/cfnguardian/compile.rb', line 64

def resources
  @resources
end

#topicsObject (readonly)

Returns the value of attribute topics.



64
65
66
# File 'lib/cfnguardian/compile.rb', line 64

def topics
  @topics
end

Instance Method Details

#alarmsObject



185
186
187
# File 'lib/cfnguardian/compile.rb', line 185

def alarms
  @resources.select {|resource| resource.type == 'Alarm'}
end

#compile_templates(template_file) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
# File 'lib/cfnguardian/compile.rb', line 212

def compile_templates(template_file)      
  main_stack = CfnGuardian::Stacks::Main.new()
  main_stack.build_template(@stacks,@checks,@topics,@maintenance_groups,@ssm_parameters)
  
  resource_stack = CfnGuardian::Stacks::Resources.new(main_stack.template)
  resource_stack.build_template(@resources)

  valid = main_stack.template.validate
  FileUtils.mkdir_p 'out'
  File.write("out/#{template_file}", JSON.parse(valid.to_json).to_yaml)
end

#generate_default_event_subscriptionsObject



261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/cfnguardian/compile.rb', line 261

def generate_default_event_subscriptions()
  # List of Classes which default events should be deployed
  default_resource_classes = ['CfnGuardian::Resource::Acm']
  default_event_subscriptions = []

  default_resource_classes.each do |resource_class|
    resource_instance = Kernel.const_get(resource_class).new({"Id"=>resource_class}) # Dummy ID 
    default_event_subscriptions.concat(resource_instance.default_event_subscriptions())
  end

  return default_event_subscriptions
end

#genrate_template_config(parameters) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/cfnguardian/compile.rb', line 243

def genrate_template_config(parameters)
  template = {
    Tags: {
      'guardian:version': CfnGuardian::VERSION
    }
  }

  if ENV.has_key?('CODEBUILD_RESOLVED_SOURCE_VERSION')
    template[:Tags][:'guardian:config:commit'] = ENV['CODEBUILD_RESOLVED_SOURCE_VERSION']
  end

  unless parameters.empty?
    template[:Parameters] = parameters
  end

  File.write("out/template-config.guardian.json", template.to_json)
end

#get_resourcesObject



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/cfnguardian/compile.rb', line 92

def get_resources
  @resource_groups.each do |group,resources|
    resources.each do |resource|
      if !resource.has_key?('Id')
        if !resource.has_key?('Hosts')
          @errors << "CfnGuardian::NoIdKeyForResourceError - resource: #{resource} in resource group: #{group} doesn't have the `Id:` key"
          next
        else
          resource['Hosts'].each { |host|
            if !host.has_key?('Id')
              @errors << "CfnGuardian::NoIdKeyForResourceError - resource: #{resource} in resource group: #{group} doesn't have the `Id:` key"
              next
            end
          }
          
        end
      end
      
      begin
        resource_class = Kernel.const_get("CfnGuardian::Resource::#{group}").new(resource)
      rescue NameError => e
        if @templates.has_key?(group) && @templates[group].has_key?('Inherit')
          begin
            resource_class = Kernel.const_get("CfnGuardian::Resource::#{@templates[group]['Inherit']}").new(resource, group)
            logger.debug "Inherited resource group #{@templates[group]['Inherit']} for group #{group}"
          rescue NameError => e
            logger.warn "'#{@templates[group]['Inherit']}' resource group doesn't exist and is unable to be inherited from"
            next
          end
        else
          logger.error(e)
          next
        end
      end

      if @check_resources_exist && !resource_class.resource_exists?
        @errors << "CfnGuardian::ResourceNotExistsError - #{group} #{resource['Id']} doesn't exist"
      end
      
      template_overides = @templates.has_key?(group) ? @templates[group] : {}
      @resources.concat resource_class.get_alarms(group,template_overides)

      @resources.concat resource_class.get_metric_filters()
      @resources.concat resource_class.get_events()

      event_subscriptions = @event_subscriptions.has_key?(group) ? @event_subscriptions[group] : {}
      @resources.concat resource_class.get_event_subscriptions(group,event_subscriptions)
      
      @checks.concat resource_class.get_checks()

      @cost += resource_class.get_cost
    end
  end

  # Add default event subscriptions
  @resources.concat generate_default_event_subscriptions()
  
  @maintenance_groups.each do |maintenance_group,resource_groups|
    resource_groups.each do |group, alarms|
      if group == 'Schedules' 
        next
      end
      alarms.each do |alarm, resources|
        resources.each do |resource|

          res = @resources.find {|r| 
            (r.type == 'Alarm') && 
            (r.group == group && r.name == alarm) &&
            (r.resource_id == resource['Id'] || (!resource['Name'].nil? && r.resource_name == resource['Name']))}

          unless res.nil?
            res.maintenance_groups.append("#{maintenance_group}MaintenanceGroup")
          end
          
        end
      end
    end
  end
  
  @composites.each do |name,params|
    @resources.push CfnGuardian::Models::Composite.new(name,params)
    @cost += 0.50
  end
  
  @ssm_parameters = @resources.select {|resource| resource.type == 'Event'}.map {|event| event.ssm_parameters}.flatten.uniq

  validate_resources()

  if @errors.any?
    raise CfnGuardian::ValidationError, "#{@errors.size} errors found\n[*] #{@errors.join("\n[*] ")}"
  end
end

#load_parameters(options) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/cfnguardian/compile.rb', line 224

def load_parameters(options)
  parameters = {}
  # Load sns topic parameters in order of preference
  @topics.each do |key, value|
    # if parameter is passed in as a command line option
    if options.has_key?("sns_#{key.downcase}")
      parameters[key.to_sym] = options["sns_#{key.downcase}"]
    # if parameter is in config
    elsif !value.empty?
      parameters[key.to_sym] = value
    # if parameter is set as environment variable
    elsif ENV.has_key?("GUARDIAN_TOPIC_#{key.upcase}")
      parameters[key.to_sym] = ENV["GUARDIAN_TOPIC_#{key.upcase}"]
    end
  end

  return parameters
end

#validate_resourcesObject



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/cfnguardian/compile.rb', line 189

def validate_resources()
  @resources.each do |resource|
    case resource.type
    when 'Alarm'
      %w(metric_name namespace).each do |property|
        if resource.send(property).nil?
          @errors << "CfnGuardian::AlarmPropertyError - alarm #{resource.name} for resource #{resource.resource_id} has nil value for property #{property.to_camelcase}. This could be due to incorrect spelling of a default alarm name or missing property #{property.to_camelcase} on a new alarm."
        end
      end
    when 'Check'
      # no validation check yet
    when 'Event'
      # no validation check yet
    when 'Composite'
      # no validation check yet
    when 'EventSubscription'
      # no validation check yet
    when 'MetricFilter'
      # no validation check yet
    end
  end
end