class CfnGuardian::Compile

Attributes

cost[R]
resources[R]
topics[R]

Public Class Methods

new(config_file) click to toggle source
# File lib/cfnguardian/compile.rb, line 57
def initialize(config_file)
  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', {})
  
  # 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

Public Instance Methods

alarms() click to toggle source
# File lib/cfnguardian/compile.rb, line 148
def alarms
  @resources.select {|resource| resource.type == 'Alarm'}
end
clean_out_directory() click to toggle source
# File lib/cfnguardian/compile.rb, line 208
def clean_out_directory
  Dir["out/*.yaml"].each {|file| File.delete(file)}
end
compile_templates(bucket,path) click to toggle source
# File lib/cfnguardian/compile.rb, line 190
def compile_templates(bucket,path)
  clean_out_directory()
  resources = split_resources(bucket,path)
  
  main_stack = CfnGuardian::Stacks::Main.new()
  main_stack.build_template(@stacks,@checks,@topics,@maintenance_groups,@ssm_parameters)
  valid = main_stack.template.validate
  FileUtils.mkdir_p 'out'
  File.write("out/guardian.compiled.yaml", JSON.parse(valid.to_json).to_yaml)
  
  resources.each_with_index do |resources,index|
    stack = CfnGuardian::Stacks::Resources.new(main_stack.parameters,index)
    stack.build_template(resources)
    valid = stack.template.validate
    File.write("out/guardian-stack-#{index}.compiled.yaml", JSON.parse(valid.to_json).to_yaml)
  end
end
genrate_template_config(parameters) click to toggle source
# File lib/cfnguardian/compile.rb, line 231
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_resources() click to toggle source
# File lib/cfnguardian/compile.rb, line 80
def get_resources
  @resource_groups.each do |group,resources|
    resources.each do |resource|
      
      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 "Inheritited 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
      
      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
  
  @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'] || 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()
end
load_parameters(options) click to toggle source
# File lib/cfnguardian/compile.rb, line 212
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
split_resources(bucket,path) click to toggle source
# File lib/cfnguardian/compile.rb, line 178
def split_resources(bucket,path)
  split = @resources.each_slice(200).to_a
  split.each_with_index do |resources,index|
    @stacks.push({
      'Name' => "GuardianStack#{index}",
      'TemplateURL' => "https://#{bucket}.s3.amazonaws.com/#{path}/guardian-stack-#{index}.compiled.yaml",
      'Reference' => index
    })
  end
  return split
end
validate_resources() click to toggle source
# File lib/cfnguardian/compile.rb, line 152
def validate_resources()
  errors = []
  @resources.each do |resource|
    case resource.type
    when 'Alarm'
      %w(metric_name namespace).each do |property|
        if resource.send(property).nil?
          errors << "Alarm #{resource.name} for resource #{resource.resource_id} has nil value for property #{property.to_camelcase}"
        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

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