diff --git a/app/models/fluentd/agent/common.rb b/app/models/fluentd/agent/common.rb index b82b11c..ad14daf 100644 --- a/app/models/fluentd/agent/common.rb +++ b/app/models/fluentd/agent/common.rb @@ -18,6 +18,8 @@ # - https://github.com/treasure-data/omnibus-td-agent/blob/master/templates/etc/systemd/td-agent.service.erb#L14 # fluentd: /etc/fluent/fluent.conf (created by fluentd -s) +require "strscan" + class Fluentd class Agent module Common @@ -142,6 +144,39 @@ class Fluentd FileUtils.rm(file) if File.exist? file end end + + def parse_config(config_content) + scanner = StringScanner.new(config_content) + contents = Hash.new {|h, k| h[k] = [] } + until scanner.eos? do + started = scanner.pos + header = scanner.scan_until(/^<(source|filter|match|label)/) + section_type = scanner[1] + break unless header + case section_type + when "source", "filter", "match" + current_source = header + scanner.scan_until(%r{^}) + contents[section_type] << { pos: started, content: current_source.strip } + when "label" + scanner.scan(/ ([^\s]+?)>/) + label = scanner[1] + sections = Hash.new {|h, k| h[k] = [] } + loop do + break if scanner.match?(%r{\s+?}) + section_pos = scanner.pos + section_header = scanner.scan_until(/^\s*<(filter|match)/) + section_type = scanner[1] + section_source = section_header + scanner.scan_until(%r{^\s*}) + sections[section_type] << { pos: section_pos ,content: section_source.sub(/\n/, "")} + end + scanner.scan_until(%r{^}) + contents["label:#{label}"] << { pos: started, sections: sections } + else + raise TypeError, "Unknown section: #{started}: #{section_type}" + end + end + contents + end end end end diff --git a/test/models/fluentd/agent_common_test.rb b/test/models/fluentd/agent_common_test.rb new file mode 100644 index 0000000..015f51b --- /dev/null +++ b/test/models/fluentd/agent_common_test.rb @@ -0,0 +1,170 @@ +require "test_helper" + +class Fluentd + class TestAgentCommon < ActiveSupport::TestCase + class DummyAgent + include ::Fluentd::Agent::Common + end + + setup do + @agent = DummyAgent.new + end + + sub_test_case "#parse_config" do + test "empty" do + actual = @agent.__send__(:parse_config, "") + assert_equal({}, actual) + end + + test "simple" do + actual = @agent.__send__(:parse_config, fixture_content("config/simple.conf")) + source_section_content = <<-CONFIG.strip_heredoc.chomp + + @type dummy + tag dummy + + CONFIG + filter_section_content = <<-CONFIG.strip_heredoc.chomp + + @type stdout + + CONFIG + match_section_content = <<-CONFIG.strip_heredoc.chomp + + @type stdout + + CONFIG + expected = { + "source" => [ + { pos: 0, content: source_section_content } + ], + "filter" => [ + { pos: 44, content: filter_section_content } + ], + "match" => [ + { pos: 84, content: match_section_content } + ] + } + assert_equal(expected, actual) + end + + test "simple label" do + actual = @agent.__send__(:parse_config, fixture_content("config/label.conf")) + source_section_content = <<-CONFIG.strip_heredoc.chomp + + @type dummy + tag dummy + @label @INPUT + + CONFIG + filter_section_content = <<-CONFIG.chomp + + @type stdout + + CONFIG + match_section_content = <<-CONFIG.chomp + + @type stdout + + CONFIG + sections = { + "filter" => [ + { pos: 75, content: filter_section_content} + ], + "match" => [ + { pos: 121, content: match_section_content} + ] + } + expected = { + "source" => [ + { pos: 0, content: source_section_content } + ], + "label:@INPUT" => [ + { pos: 60, sections: sections } + ] + } + assert_equal(expected, actual) + end + + test "multiple labels" do + actual = @agent.__send__(:parse_config, fixture_content("config/multi-label.conf")) + source_section_content1 = <<-CONFIG.strip_heredoc.chomp + + @type dummy + tag dummy1 + @label @INPUT + + CONFIG + source_section_content2 = <<-CONFIG.strip_heredoc.chomp + + @type dummy + tag dummy2 + @label @INPUT + + CONFIG + filter_section_content = <<-CONFIG.chomp + + @type stdout + + CONFIG + match_section_content = <<-CONFIG.chomp + + @type relabel + @label @MAIN + + CONFIG + input_sections = { + "filter" => [ + { pos: 140, content: filter_section_content } + ], + "match" => [ + { pos: 187, content: match_section_content } + ] + } + filter_secion_content1 = <<-CONFIG.chomp + + @type stdout + + CONFIG + filter_secion_content2 = <<-CONFIG.chomp + + @type stdout + + CONFIG + match_secion_content1 = <<-CONFIG.chomp + + @type stdout + + CONFIG + match_secion_content2 = <<-CONFIG.chomp + + @type stdout + + CONFIG + main_sections = { + "filter" => [ + { pos: 274, content: filter_secion_content1 }, + { pos: 321, content: filter_secion_content2 } + ], + "match" => [ + { pos: 368, content: match_secion_content1 }, + { pos: 413, content: match_secion_content2 } + ] + } + expected = { + "source" => [ + { pos: 0, content: source_section_content1 }, + { pos: 61, content: source_section_content2 }, + ], + "label:@INPUT" => [ + { pos: 124, sections: input_sections } + ], + "label:@MAIN" => [ + { pos: 259, sections: main_sections } + ] + } + assert_equal(expected, actual) + end + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index fa9ba09..2a39d81 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -18,6 +18,10 @@ module FixturePath def fixture_path(fixture_name) Rails.root.join("test/fixtures", fixture_name).to_s end + + def fixture_content(fixture_name) + Rails.root.join("test/fixtures", fixture_name).read + end end class ActiveSupport::TestCase