require 'yaml' module Flydata module Helper class ConfigError < StandardError end # This module parses a config file and return a key symbolized hash. # Additionally config for the helper is replaced with Config instance # # ex) helper.conf # ============================================ # # For helper(serverengine) # workers: 2 # # For helper # helper: # scheduled_actions: # check_remote_actions: # check_interval: 10s # ============================================ class Config < Hash DEFAULT_INTERVAL = 30.0 DEFAULT_SCHEDULED_ACTIONS = { check_remote_actions: { check_interval: '30s', }, } DEFAULT_HELPER = { scheduled_actions: DEFAULT_SCHEDULED_ACTIONS, } DEFAULT_CONFIG = { helper: DEFAULT_HELPER } def self.create(hash) self.new.merge(hash) end def self.default create(DEFAULT_HELPER) end def fetch_scheduled_actions_conf(action_name, name, default = nil) value = if self[:scheduled_actions] and self[:scheduled_actions][action_name.to_sym] and self[:scheduled_actions][action_name.to_sym][name.to_sym] self[:scheduled_actions][action_name.to_sym][name.to_sym] else nil end value.nil? ? default : value end class << self # snip from https://github.com/fluent/fluentd/blob/master/lib/fluent/config.rb#L119 def time_value(str) case str.to_s when /([0-9]+)s/ $~[1].to_i when /([0-9]+)m/ $~[1].to_i * 60 when /([0-9]+)h/ $~[1].to_i * 60*60 when /([0-9]+)d/ $~[1].to_i * 24*60*60 else str.to_f end end def convert_format(format, value) return nil if value.nil? case format when :time self.time_value(value) when :integer value.to_i when :float value.to_f when :string value.to_s when :bool case value when 'true' true when 'false' false else !!(value) end else raise "Invalid format:#{format}" end end def config_param(name, format, option = {}) method_name = name.to_s key = option[:key] || name default_value = option[:default] case option[:type] when :scheduled_actions define_method(method_name) do |action_name| Config.convert_format(format, fetch_scheduled_actions_conf( action_name, key, default_value)) end else define_method(method_name) do def_val = if default_value.respond_to?(:call) default_value.call(self) else default_value end Config.convert_format(format, self[key] || def_val) end end end end config_param :helper_retry_alert_limit, :integer, default: 3 config_param :helper_retry_interval, :float, default: 10 config_param :helper_retry_limit, :integer, default: 15 # helper directories config_param :helper_home, :string, default: FLYDATA_HELPER_HOME config_param :helper_pid_dir, :string, default: lambda{|c| File.join(c.helper_home, 'pids') } config_param :helper_position_dir, :string, default: lambda{|c| File.join(c.helper_home, 'positions') } # helper files config_param :helper_action_position_path, :string, default: lambda{|c| File.join(c.helper_position_dir, 'helper_action.pos') } config_param :helper_log_path, :string, default: FLYDATA_LOG config_param :helper_pid_path, :string, default: lambda{|c| File.join(c.helper_pid_dir, 'helper.pid') } # Return deep copy def scheduled_actions actions = self[:scheduled_actions].dup self[:scheduled_actions].each do |k, v| actions[k] = v.dup actions[k][:name] = k actions[k][:check_interval] = Config.convert_format(:time, v) end actions end end class ConfigParser def self.parse_file(config_path = nil) config_path.nil? ? { helper: Config.default } : parse(File.open(config_path).read) end def self.parse(config_content) ConfigParser.new.parse(config_content) end def parse(config_content) conf = YAML::load(config_content) raise ConfigError.new("Invalid conf format.") unless conf and conf.kind_of?(Hash) conf = symbolize_keys(conf) conf[:helper] = Config.create(parse_helper(conf[:helper])) conf end def parse_helper(conf) return Config::DEFAULT_HELPER if conf.nil? or conf.empty? conf[:scheduled_actions] = Config::DEFAULT_SCHEDULED_ACTIONS. dup.merge(conf[:scheduled_actions] || {}) conf end def symbolize_keys(value) case value when Hash value.inject({}){|result, (key, value)| new_key = case key when String then key.to_sym else key end new_value = symbolize_keys(value) result[new_key] = new_value result } when Array value.collect {|e| symbolize_keys(e)} else value end end end end end