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{^(?:source|filter|match)>})
+ 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*(?:filter|match)>})
+ 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