Skip to content

Commit

Permalink
yaml config file
Browse files Browse the repository at this point in the history
Signed-off-by: Yuta Iwama <[email protected]>
  • Loading branch information
ganmacs committed Apr 2, 2020
1 parent 1f3e685 commit 8606218
Show file tree
Hide file tree
Showing 8 changed files with 438 additions and 2 deletions.
8 changes: 8 additions & 0 deletions lib/fluent/command/fluentd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@
opts[:without_source] = b
}

op.on('--config-file-type VALU', 'file type of fluentd. yaml or yml') { |s|
if (s == 'yaml') || (s == 'yml')
opts[:config_file_type] = s.to_sym
else
usage '--config-file-type accepts yaml or yml'
end
}

op.on('--use-v1-config', "Use v1 configuration format (default)", TrueClass) {|b|
opts[:use_v1_config] = b
}
Expand Down
8 changes: 7 additions & 1 deletion lib/fluent/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require 'fluent/config/error'
require 'fluent/config/element'
require 'fluent/configurable'
require 'fluent/config/yaml_parser'

module Fluent
module Config
Expand All @@ -25,7 +26,11 @@ module Config
# @param additional_config [String] config which is added to last of config body
# @param use_v1_config [Bool] config is formatted with v1 or not
# @return [Fluent::Config]
def self.build(config_path:, encoding: 'utf-8', additional_config: nil, use_v1_config: true)
def self.build(config_path:, encoding: 'utf-8', additional_config: nil, use_v1_config: true, type: nil)
if type == :yaml
return Fluent::Config::YamlParser.parse(config_path)
end

config_fname = File.basename(config_path)
config_basedir = File.dirname(config_path)
config_data = File.open(config_path, "r:#{encoding}:utf-8") do |f|
Expand All @@ -36,6 +41,7 @@ def self.build(config_path:, encoding: 'utf-8', additional_config: nil, use_v1_c
end
s
end

Fluent::Config.parse(config_data, config_fname, config_basedir, use_v1_config)
end

Expand Down
44 changes: 44 additions & 0 deletions lib/fluent/config/yaml_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'fluent/config/yaml_parser/loader'
require 'fluent/config/yaml_parser/parser'
require 'pathname'

module Fluent
module Config
module YamlParser
def self.parse(path)
context = Kernel.binding

unless context.respond_to?(:use_nil)
context.define_singleton_method(:use_nil) do
raise Fluent::SetNil
end
end

unless context.respond_to?(:use_default)
context.define_singleton_method(:use_default) do
raise Fluent::SetDefault
end
end

s = Fluent::Config::YamlParser::Loader.new(context).load(Pathname.new(path))
Fluent::Config::YamlParser::Parser.new(s).build.to_element
end
end
end
end
47 changes: 47 additions & 0 deletions lib/fluent/config/yaml_parser/fluent_value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

module Fluent
module Config
module YamlParser
module FluentValue
JsonValue = Struct.new(:val) do
def to_s
val.to_json
end

def to_element
to_s
end
end

StringValue = Struct.new(:val, :context) do
def to_s
context.instance_eval("\"#{val}\"")
rescue Fluent::SetNil => _
''
rescue Fluent::SetDefault => _
':default'
end

def to_element
to_s
end
end
end
end
end
end
91 changes: 91 additions & 0 deletions lib/fluent/config/yaml_parser/loader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'psych'
require 'json'
require 'fluent/config/error'
require 'fluent/config/yaml_parser/fluent_value'

# Based on https://github.com/eagletmt/hako/blob/34cdde06fe8f3aeafd794be830180c3cedfbb4dc/lib/hako/yaml_loader.rb

module Fluent
module Config
module YamlParser
class Loader
INCLUDE_TAG = 'tag:include'.freeze
FLUENT_JSON_TAG = 'tag:fluent/json'.freeze
FLUENT_STR_TAG = 'tag:fluent/s'.freeze
SHOVEL = '<<'.freeze

def initialize(context = Kernel.binding)
@context = context
@current_path = nil
end

# @param [String] path
# @return [Hash]
def load(path)
class_loader = Psych::ClassLoader.new
scanner = Psych::ScalarScanner.new(class_loader)

visitor = Visitor.new(scanner, class_loader)

visitor._register_domain(INCLUDE_TAG) do |_, val|
load(path.parent.join(val))
end

visitor._register_domain(FLUENT_JSON_TAG) do |_, val|
Fluent::Config::YamlParser::FluentValue::JsonValue.new(val)
end

visitor._register_domain(FLUENT_STR_TAG) do |_, val|
Fluent::Config::YamlParser::FluentValue::StringValue.new(val, @context)
end

path.open do |f|
visitor.accept(Psych.parse(f))
end
end

class Visitor < Psych::Visitors::ToRuby
def initialize(scanner, class_loader)
super(scanner, class_loader)
end

def _register_domain(name, &block)
@domain_types.merge!({ name => [name, block] })
end

def revive_hash(hash, o)
super(hash, o).tap do |r|
if r[SHOVEL].is_a?(Hash)
h2 = {}
r.each do |k, v|
if k == SHOVEL
h2.merge!(v)
else
h2[k] = v
end
end
r.replace(h2)
end
end
end
end
end
end
end
end
126 changes: 126 additions & 0 deletions lib/fluent/config/yaml_parser/parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'fluent/config/yaml_parser/section_builder'

module Fluent
module Config
module YamlParser
class Parser
def initialize(config, indent: 2)
@base_indent = indent
@config = config
end

def build
s = @config['system'] && system_config_build(@config['system'])
c = @config['config'] && config_build(@config['config'], root: true)
RootBuilder.new(s, c)
end

private

def system_config_build(config)
section_build('system', config)
end

def config_build(config, indent: 0, root: false)
sb = SectionBodyBuilder.new(indent, root: root)
config.each do |c|
if (lc = c.delete('label'))
sb.add_section(label_build(lc, indent: indent))
end

if (sc = c.delete('source'))
sb.add_section(source_build(sc, indent: indent))
end

if (fc = c.delete('filter'))
sb.add_section(filter_build(fc, indent: indent))
end

if (mc = c.delete('match'))
sb.add_section(match_build(mc, indent: indent))
end

if (wc = c.delete('worker'))
sb.add_section(worker_build(wc, indent: indent))
end
end

sb
end

def label_build(config, indent: 0)
config = config.dup
name = config.delete('$name')
c = config.delete('config')
SectionBuilder.new('label', config_build(c, indent: indent + @base_indent), indent, name)
end

def worker_build(config, indent: 0)
config = config.dup
num = config.delete('$arg')
c = config.delete('config')
SectionBuilder.new('worker', config_build(c, indent: indent + @base_indent), indent, num)
end

def source_build(config, indent: 0)
section_build('source', config, indent: indent)
end

def filter_build(config, indent: 0)
config = config.dup
tag = config.delete('$tag')
section_build('filter', config, indent: indent, arg: tag)
end

def match_build(config, indent: 0)
config = config.dup
tag = config.delete('$tag')
section_build('match', config, indent: indent, arg: tag)
end

def section_build(name, config, indent: 0, arg: nil)
sb = SectionBodyBuilder.new(indent + @base_indent)

if (v = config.delete('$type'))
sb.add_line('@type', v)
end

if (v = config.delete('$label'))
sb.add_line('@label', v)
end

config.each do |key, val|
if val.is_a?(Array)
val.each do |v|
sb.add_section(section_build(key, v, indent: indent + @base_indent))
end
elsif val.is_a?(Hash)
harg = val.delete('$arg')
sb.add_section(section_build(key, val, indent: indent + @base_indent, arg: harg))
else
sb.add_line(key, val)
end
end

SectionBuilder.new(name, sb, indent, arg)
end
end
end
end
end
Loading

0 comments on commit 8606218

Please sign in to comment.