Skip to content

Commit

Permalink
add DSL Config parser
Browse files Browse the repository at this point in the history
  • Loading branch information
tagomoris committed Aug 12, 2013
1 parent 5c4ee06 commit 01cdc5f
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 0 deletions.
59 changes: 59 additions & 0 deletions lib/fluentd/config/dsl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module Fluentd
module Config
module DSL
module DSLParser
def self.read(path)
path = File.expand_path(path)
data = File.read(path)
parse(data, path)
end

def self.parse(source, source_path="config.rb")
DSLElement.new('ROOT', nil).instance_eval(source, source_path).__config_element
end

end

class DSLElement
attr_accessor :name, :arg, :attrs, :elements

def initialize(name, arg)
@name = name
@arg = arg
@attrs = {}
@elements = []
end

def __config_element
Fluentd::Config::Element.new(@name, @arg, @attrs, @elements)
end

def method_missing(name, *args)
raise ArgumentError, "Configuration DSL Syntax Error: only one argument allowed" if args.size > 1
value = args.first
@attrs[name.to_s] = value.is_a?(Symbol) ? value.to_s : value
self
end

def __element(name, arg, block)
raise ArgumentError, "#{name} block must be specified" if block.nil?
element = DSLElement.new(name, arg)
element.instance_exec(&block)
@elements.push(element.__config_element)
self
end

def __need_arg(name, args)
raise ArgumentError, "#{name} block requires arguments for match pattern" if args.nil? || args.size != 1
true
end

def worker(&block); __element('worker', nil, block); end
def source(&block); __element('source', nil, block); end
def filter(*args, &block); __need_arg('filter', args); __element('filter', args.first, block); end
def match(*args, &block); __need_arg('match', args); __element('match', args.first, block); end
def label(*args, &block); __need_arg('label', args); __element('label', args.first, block); end

This comment has been minimized.

Copy link
@udzura

udzura Aug 12, 2013

What is the reason to use name mangling rather than private keyword ......???

This comment has been minimized.

Copy link
@tagomoris

tagomoris Aug 12, 2013

Author Member

Oh, it's right suggestion.

This comment has been minimized.

Copy link
@shishi

shishi Aug 12, 2013

👍

This comment has been minimized.

Copy link
@yuya-takeyama

yuya-takeyama Aug 12, 2013

In this implementation, configuration written in DSL seems to be evaluated in the context of DSLElement instance.
So methods defined as private also could be callable from configuration file, couldn't that?

This comment has been minimized.

Copy link
@udzura

udzura Aug 12, 2013

😙

This comment has been minimized.

Copy link
@tagomoris

tagomoris Aug 12, 2013

Author Member

fixed at dacb875

This comment has been minimized.

Copy link
@tagomoris

tagomoris Aug 12, 2013

Author Member

Ahhhhhh, I made a wrong decision.

Private method 'element' raise errors like private methodelement' called for ...`.
It blocks to make <element> configuration subsections on our configuration.

We should name methods as mangled, and also should define it as private.

Thoughts?

This comment has been minimized.

Copy link
@udzura

udzura Aug 12, 2013

I __understand__ ...

end
end
end
end
4 changes: 4 additions & 0 deletions lib/fluentd/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ def after_run

# read configuration file
def read_config(path)
if path =~ /\.rb$/
return Config::DSL::DSLParser.read(path)
end

begin
# try to use v11 mode first
conf = Config::Parser.read(path)
Expand Down
70 changes: 70 additions & 0 deletions spec/config/dsl_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
require_relative "./helper"

require 'fluentd/config/element'
require "fluentd/config/dsl"

DSL_CONFIG_EXAMPLE = %q[
worker {
hostname = "myhostname"
(0..9).each { |i|
source {
type :tail
path "/var/log/httpd/access.part#{i}.log"
filter ('bar.**') {
type :hoge
val1 "moge"
val2 ["foo", "bar", "baz"]
val3 10
id :hoge
}
filter ('foo.**') {
type "pass"
}
match ('{foo,bar}.**') {
type "file"
path "/var/log/httpd/access.#{hostname}.#{i}.log"
}
}
}
}
]

describe Fluentd::Config::DSL::DSLParser do
include_context 'config_helper'

def parse_dsl(text)
Fluentd::Config::DSL::DSLParser.parse(text)
end

def e(name, arg='', attrs={}, elements=[])
Fluentd::Config::Element.new(name, arg, attrs, elements)
end

describe '.parse' do
it 'can parse simple dsl style configuration' do
root = parse_dsl(DSL_CONFIG_EXAMPLE)
expect(root.name).to eql('ROOT')
expect(root.arg).to be_nil
expect(root.keys.size).to eql(0)
expect(root.elements.size).to eql(1)

worker = root.elements.first
expect(worker.name).to eql('worker')
expect(worker.arg).to be_nil
expect(worker.keys.size).to eql(0)
expect(worker.elements.size).to eql(10)

ele4 = worker.elements[4]
expect(ele4.name).to eql('source')
expect(ele4.arg).to be_nil
expect(ele4.keys.size).to eql(2)
expect(ele4['type']).to eql('tail')
expect(ele4['path']).to eql("/var/log/httpd/access.part4.log")
end
end

end

3 comments on commit 01cdc5f

@yuya-takeyama
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job 👍

@y-ken
Copy link
Member

@y-ken y-ken commented on 01cdc5f Aug 12, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic 👍

@sonots
Copy link
Member

@sonots sonots commented on 01cdc5f Aug 12, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome 👍

Please sign in to comment.