config_init

in logstash-core/lib/logstash/config/mixin.rb [61:624]


  def config_init(params)
    
    

    
    
    
    original_params = params.clone

    
    @plugin_type = self.class.ancestors.find { |a| a.name =~ /::Base$/ }.config_name

    
    self.class.get_config.each do |name, opts|
      next if params.include?(name.to_s)
      if opts.include?(:default) and (name.is_a?(Symbol) or name.is_a?(String))
        
        
        case opts[:default]
          when FalseClass, TrueClass, NilClass, Numeric
            params[name.to_s] = opts[:default]
          else
            params[name.to_s] = opts[:default].clone
        end
      end

      
      if self.class.default?(name)
        params[name.to_s] = self.class.get_default(name)
      end
    end

    
    params.each do |name, value|
      params[name.to_s] = deep_replace(value, true)
    end

    
    params.each do |name, value|
      validator = self.class.validator_find(name)
      next unless validator && validator[:validate] == :codec && value.kind_of?(String)

      codec_klass = LogStash::Plugin.lookup("codec", value)
      codec_instance = LogStash::Plugins::Contextualizer.initialize_plugin(execution_context, codec_klass)

      params[name.to_s] = LogStash::Codecs::Delegator.new(codec_instance)
    end

    if !self.class.validate(params)
      raise LogStash::ConfigurationError,
        I18n.t("logstash.runner.configuration.invalid_plugin_settings")
    end

    
    
    self.class.secure_params!(original_params)
    @original_params = original_params

    
    original_params.each do |name, value|
      opts = self.class.get_config[name]
      if opts && opts[:deprecated]
        extra = opts[:deprecated].is_a?(String) ? opts[:deprecated] : ""
        extra.gsub!("%PLUGIN%", self.class.config_name)
        self.deprecation_logger.deprecated(
          "You are using a deprecated config setting #{name.inspect} set in " \
          "#{self.class.config_name}. Deprecated settings will continue to work, " \
          "but are scheduled for removal from logstash in the future. #{extra} " \
          "If you have any questions about this, please ask it on the " \
          "https://discuss.elastic.co/c/logstash discussion forum",
          {}
        )
      end

      if opts && opts[:obsolete]
        extra = opts[:obsolete].is_a?(String) ? opts[:obsolete] : ""
        extra.gsub!("%PLUGIN%", self.class.config_name)
        raise LogStash::ConfigurationError,
          I18n.t("logstash.runner.configuration.obsolete", :name => name,
                 :plugin => self.class.config_name, :extra => extra)
      end
    end

    
    
    
    
    
    params.reject! do |name, value|
      opts = self.class.get_config[name]
      opts.include?(:obsolete)
    end

    
    params.each do |key, value|
      next if key[0, 1] == "@"

      
      self.logger.debug("config #{self.class.name}/@#{key} = #{value.inspect}")
      instance_variable_set("@#{key}", value)
    end

    @config = params
  end 

  module DSL
    include LogStash::Util::SubstitutionVariables

    attr_accessor :flags

    
    
    def config_name(name = nil)
      @config_name = name if !name.nil?
      @config_name
    end
    alias_method :config_plugin, :config_name

    
    
    def plugin_status(status = nil)
      milestone(status)
    end

    
    
    def milestone(m = nil)
      self.logger.debug(I18n.t('logstash.plugin.deprecated_milestone', :plugin => config_name))
    end

    
    
    
    
    
    
    
    
    
    
    def config(name, opts = {})
      @config ||= Hash.new
      

      name = name.to_s if name.is_a?(Symbol)
      @config[name] = opts  

      if name.is_a?(String) && opts.fetch(:attr_accessor, true)
        define_method(name) { instance_variable_get("@#{name}") }
        define_method("#{name}=") { |v| instance_variable_set("@#{name}", v) }
      end
    end 

    def default(name, value)
      @defaults ||= {}
      @defaults[name.to_s] = value
    end

    def get_config
      return @config
    end 

    def get_default(name)
      return @defaults && @defaults[name]
    end

    def default?(name)
      return @defaults && @defaults.include?(name)
    end

    def options(opts)
      
      prefix = self.name.split("::").last.downcase
      @flags.each do |flag|
        flagpart = flag[:args].first.gsub(/^--/, "")
        

        opts.on("--#{prefix}-#{flagpart}", *flag[:args][1..-1], &flag[:block])
      end
    end 

    
    def inherited(subclass)
      
      
      
      subconfig = Hash.new
      if !@config.nil?
        @config.each do |key, val|
          subconfig[key] = val
        end
      end
      subclass.instance_variable_set("@config", subconfig)
    end 

    def validate(params)
      @plugin_name = config_name
      @plugin_type = ancestors.find { |a| a.name =~ /::Base$/ }.config_name
      is_valid = true

      is_valid &&= validate_check_invalid_parameter_names(params)
      is_valid &&= validate_check_required_parameter_names(params)
      is_valid &&= validate_check_parameter_values(params)

      return is_valid
    end 

    def validate_check_invalid_parameter_names(params)
      invalid_params = params.keys
      
      
      
      @config.each_key do |config_key|
        if config_key.is_a?(Regexp)
          invalid_params.reject! { |k| k =~ config_key }
        elsif config_key.is_a?(String)
          invalid_params.reject! { |k| k == config_key }
        end
      end

      if invalid_params.size > 0
        invalid_params.each do |name|
          self.logger.error("Unknown setting '#{name}' for #{@plugin_name}")
        end
        return false
      end 
      return true
    end 

    def validate_check_required_parameter(config_key, config_opts, k, v)
      if config_key.is_a?(Regexp)
        (k =~ config_key && v)
      elsif config_key.is_a?(String)
        k && v
      end
    end

    def validate_check_required_parameter_names(params)
      is_valid = true

      @config.each do |config_key, config|
        next unless config[:required]

        if config_key.is_a?(Regexp) && !params.keys.any? { |k| k =~ config_key }
          is_valid = false
        end

        value = params[config_key]
        if value.nil? || (config[:list] && Array(value).empty?)
          self.logger.error(I18n.t("logstash.runner.configuration.setting_missing",
                               :setting => config_key, :plugin => @plugin_name,
                               :type => @plugin_type))
          is_valid = false
        end
      end

      return is_valid
    end

    def process_parameter_value(value, config_settings)
      config_val = config_settings[:validate]

      if config_settings[:list]
        value = Array(value) 
        
        return true, [] if value.empty?

        return validate_value(value, :uri_list) if config_val == :uri

        validated_items = value.map {|v| validate_value(v, config_val)}
        is_valid = validated_items.all? {|sr| sr[0] }
        processed_value = validated_items.map {|sr| sr[1]}
      else
        is_valid, processed_value = validate_value(value, config_val)
      end

      return [is_valid, processed_value]
    end

    def validate_check_parameter_values(params)
      
      
      
      all_params_valid = true

      params.each do |key, value|
        @config.keys.each do |config_key|
          next unless (config_key.is_a?(Regexp) && key =~ config_key) \
                      || (config_key.is_a?(String) && key == config_key)

          config_settings = @config[config_key]

          is_valid, processed_value = process_parameter_value(value, config_settings)

          if is_valid
            
            
            params[key] = processed_value
          else
            self.logger.error(I18n.t("logstash.runner.configuration.setting_invalid",
                                 :plugin => @plugin_name, :type => @plugin_type,
                                 :setting => key, :value => value.inspect,
                                 :value_type => config_settings[:validate],
                                 :note => processed_value))
          end

          all_params_valid &&= is_valid

          break 
        end 
      end 

      return all_params_valid
    end 

    def validator_find(key)
      @config.each do |config_key, config_val|
        if (config_key.is_a?(Regexp) && key =~ config_key) \
           || (config_key.is_a?(String) && key == config_key)
          return config_val
        end
      end 
      return nil
    end

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    def validate_value(value, validator)
      
      
      
      
      result = nil

      value = deep_replace(value)

      if validator.nil?
        return true, value
      elsif validator.is_a?(Array)
        value = [*value]
        if value.size > 1
          return false, "Expected one of #{validator.inspect}, got #{value.inspect}"
        end

        if !validator.include?(value.first)
          return false, "Expected one of #{validator.inspect}, got #{value.inspect}"
        end
        result = value.first
      elsif validator.is_a?(Symbol)
        
        
        value = hash_or_array(value)

        case validator
          when :codec
            if value.first.is_a?(String)
              
              
              
              self.deprecation_logger.deprecated("Codec instantiated by `Config::Mixin::DSL::validate_value(String, :codec)` which cannot propagate parent plugin's execution context or metrics. ",
                                                 self.logger.debug? ? {:backtrace => caller} : {})
              value = LogStash::Codecs::Delegator.new LogStash::Plugin.lookup("codec", value.first).new
              return true, value
            else
              value = value.first
              return true, value
            end
          when :hash
            if value.is_a?(Hash)
              return true, value
            end

            if value.size % 2 == 1
              return false, "This field must contain an even number of items, got #{value.size}"
            end

            
            result = {}
            value.each_slice(2) do |key, value|
              entry = result[key]
              if entry.nil?
                result[key] = value
              else
                if entry.is_a?(Array)
                  entry << value
                else
                  result[key] = [entry, value]
                end
              end
            end
          when :array
            result = value
          when :string
            if value.size > 1 
              return false, "Expected string, got #{value.inspect}"
            end
            result = value.first
          when :number
            if value.size > 1 
              return false, "Expected number, got #{value.inspect} (type #{value.class})"
            end

            v = value.first
            case v
              when Numeric
                result = v
              when String
                if v.to_s.to_f.to_s != v.to_s \
                   && v.to_s.to_i.to_s != v.to_s
                  return false, "Expected number, got #{v.inspect} (type #{v})"
                end
                if v.include?(".")
                  
                  result = v.to_f
                else
                  result = v.to_i
                end
            end 
          when :boolean
            if value.size > 1 
              return false, "Expected boolean, got #{value.inspect}"
            end

            bool_value = value.first
            if !!bool_value == bool_value
              
              
              result = bool_value
            else
              if bool_value !~ /^(true|false)$/
                return false, "Expected boolean 'true' or 'false', got #{bool_value.inspect}"
              end

              result = (bool_value == "true")
            end
          when :ipaddr
            if value.size > 1 
              return false, "Expected IPaddr, got #{value.inspect}"
            end

            octets = value.split(".")
            if octets.length != 4
              return false, "Expected IPaddr, got #{value.inspect}"
            end
            octets.each do |o|
              if o.to_i < 0 or o.to_i > 255
                return false, "Expected IPaddr, got #{value.inspect}"
              end
            end
            result = value.first
          when :password
            if value.size > 1
              return false, "Expected password (one value), got #{value.size} values?"
            end

            result = value.first.is_a?(::LogStash::Util::Password) ? value.first : ::LogStash::Util::Password.new(value.first)
          when :uri
            if value.size > 1
              return false, "Expected uri (one value), got #{value.size} values?"
            end

            result = value.first.is_a?(::LogStash::Util::SafeURI) ? value.first : ::LogStash::Util::SafeURI.new(value.first)
          when :uri_list
            
            
            
            result = value.flat_map do |entry|
              entry.kind_of?(String) ? entry.split(' ') : entry
            end.map do |expanded_entry|
              ::LogStash::Util::SafeURI.from(expanded_entry)
            end
          when :path
            if value.size > 1 
              return false, "Expected path (one value), got #{value.size} values?"
            end

            
            
              
            

            if !File.exist?(value.first) 
              return false, "File does not exist or cannot be opened #{value.first}"
            end

            result = value.first
          when :bytes
            begin
              bytes = Integer(value.first) rescue nil
              result = bytes || Filesize.from(value.first).to_i
            rescue ArgumentError
              return false, "Unparseable filesize: #{value.first}. possible units (KiB, MiB, ...) e.g. '10 KiB'. doc reference: http://www.elastic.co/guide/en/logstash/current/configuration.html#bytes"
            end
          when :field_reference 
            return [false, "Expected exactly one field reference, got `#{value.inspect}`"] unless value.kind_of?(Array) && value.size <= 1
            return [true, nil] if value.empty? || value.first.nil? || value.first.empty?

            candidate = value.first

            return [false, "Expected a valid field reference, got `#{candidate.inspect}`"] unless org.logstash.FieldReference.isValid(candidate)

            return [true, candidate]
          when :sha_256_hex 
            return [false, "Expected exactly one hex-encoded SHA-256 fingerprint, got `#{value.inspect}`"] unless value.kind_of?(Array) && value.size <= 1
            return [true, nil] if value.empty? || value.first.nil? || value.first.empty?

            candidate = value.first

            return [false, "Expected a hex-encoded SHA-256 fingerprint, got `#{candidate.inspect}`"] unless candidate.kind_of?(String) && candidate =~ /\A(?:[[:xdigit:]]{2}:?){32}\z/

            return [true, candidate.upcase.tr('^0-9A-F', '')]
          else
            return false, "Unknown validator symbol #{validator}"
        end 
      else
        return false, "Unknown validator #{validator.class}"
      end

      
      return true, result
    end 

    def secure_params!(params)
      params.each do |key, value|
        if [:uri, :password].include? @config[key][:validate]
          is_valid, processed_value = process_parameter_value(value, @config[key])
          params[key] = processed_value
        end
      end
    end

    def hash_or_array(value)
      if !value.is_a?(Hash)
        value = [*value] 
      end
      return value
    end
  end 
end