Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/haproxy/config.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module HAProxy
Default = Struct.new(:name, :options, :config)
Userlist = Struct.new(:name, :options, :config)
Backend = Struct.new(:name, :options, :config, :servers)
Listener = Struct.new(:name, :host, :port, :options, :config, :servers)
Frontend = Struct.new(:name, :host, :port, :options, :config)
Expand Down Expand Up @@ -34,14 +35,15 @@ class Backend

# Represents an haproxy configuration file.
class Config
attr_accessor :original_parse_tree, :listeners, :backends, :frontends, :global, :defaults
attr_accessor :original_parse_tree, :listeners, :backends, :frontends, :global, :defaults, :userlists

def initialize(parse_tree)
self.original_parse_tree = parse_tree
self.backends = []
self.listeners = []
self.frontends = []
self.defaults = []
self.userlists = []
self.global = {}
end

Expand All @@ -53,6 +55,10 @@ def backend(name)
self.backends.find { |b| b.name == name }
end

def userlist(name)
self.userlists.find { |b| b.name == name }
end

def frontend(name)
self.frontends.find { |f| f.name == name }
end
Expand Down
15 changes: 14 additions & 1 deletion lib/haproxy/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def build_config(result)
config.backends += collect_backends(result)
config.listeners += collect_listeners(result)
config.defaults += collect_defaults(result)
config.userlists += collect_userlists(result)
end
end

Expand All @@ -64,6 +65,14 @@ def build_frontend(fs)
end
end

def build_userlist(fs)
Userlist.new.tap do |f|
f.name = try_send(fs.userlist_header, :proxy_name, :content)
f.options = options_hash_from_config_section(fs)
f.config = config_hash_from_config_section(fs)
end
end

def build_backend(bs)
Backend.new.tap do |b|
b.name = try_send(bs.backend_header, :proxy_name, :content)
Expand Down Expand Up @@ -100,6 +109,10 @@ def collect_backends(result)
result.backends.map { |bs| build_backend(bs) }
end

def collect_userlists(result)
result.userlists.map { |bs| build_userlist(bs) }
end

def collect_listeners(result)
result.listeners.map { |ls| build_listener(ls) }
end
Expand Down Expand Up @@ -180,4 +193,4 @@ def config_hash_from_config_section(cs)
end

end
end
end
74 changes: 63 additions & 11 deletions lib/haproxy/renderer.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
CONFIG_NODES = [HAProxy::Default, HAProxy::Backend, HAProxy::Listener, HAProxy::Frontend, HAProxy::Userlist]
OPTION_NODES = [HAProxy::Default, HAProxy::Backend, HAProxy::Listener, HAProxy::Frontend, HAProxy::Userlist]
SERVER_NODES = [HAProxy::Listener, HAProxy::Backend]

module HAProxy
# Responsible for rendering an HAProxy::Config instance to a string.
class Renderer
Expand All @@ -9,8 +13,10 @@ def initialize(config, source_tree)
self.source_tree = source_tree
@server_list = {}
@config_list = {}
@option_list = {}
@context = self.config
@prev_context = self.config
@linebreak_active = false
@config_text = ''
end

Expand All @@ -29,28 +35,48 @@ def render_node(node)
if e.class == HAProxy::Treetop::ServerLine
# Keep track of the servers that we've seen, so that we can detect and render new ones.
@server_list[e.name] = e

# Don't render the server element if it's been deleted from the config.
next if @context.servers[e.name].nil?
end

if e.class == HAProxy::Treetop::ConfigLine and @context.class == HAProxy::Default
# Use a custom rendering method for servers, since we allow them to be
# added/removed/changed.
render_server_element(e)
elsif e.class == HAProxy::Treetop::ConfigLine and CONFIG_NODES.include?(@context.class)
# Keep track of the configs in this config block we've seen, so that we can detect and render new ones.
@config_list[e.key] = e

# Don't render the config *if* it's been removed from the config block.
next if @context.config[e.key].nil?
end
next unless @context.config.has_key?(e.key)

if e.class == HAProxy::Treetop::ServerLine
# Use a custom rendering method for servers, since we allow them to be
# added/removed/changed.
render_server_element(e)
elsif e.class== HAProxy::Treetop::ConfigLine and @context.class == HAProxy::Default
# Use a custom rendering method for configs, since we allow them to be
# added/removed/changed.
render_config_line_element(e)
elsif e.class == HAProxy::Treetop::OptionLine and OPTION_NODES.include?(@context.class)
# Keep track of the options in this option block we've seen, so that we can detect and render new ones.
@option_list[e.key] = e

# Don't render the option *if* it's been removed from the option block.
next unless @context.options.has_key?(e.key)

# Use a custom rendering method for options, since we allow them to be
# added/removed/changed.
render_option_line_element(e)
elsif e.elements && e.elements.size > 0
render_node(e)
else
if e.class == HAProxy::Treetop::LineBreak
@linebreak_active = true
elsif @linebreak_active
if e.text_value =~ /\S/
if e.text_value.size == 1
@config_text << "\t"
end
@linebreak_active = false
else
next
end
end
@config_text << e.text_value
end
end
Expand All @@ -74,6 +100,17 @@ def render_config_line(key, value)
@config_text << "\t#{key} #{value}\n"
end

def render_option_line_element(e)
option_key = e.key.gsub(/\s+/, ' ')
option_value = @context.options[e.key]
option_value = option_value.gsub(/\s+/, ' ') if not option_value.nil?
render_option_line(option_key, option_value)
end

def render_option_line(key, value)
@config_text << "\toption #{key} #{value}\n"
end

def render_server_element(e)
server = @context.servers[e.name]
render_server(server)
Expand All @@ -85,7 +122,7 @@ def render_server(server)
end

def handle_context_change
if [HAProxy::Default].include?(@prev_context.class)
if CONFIG_NODES.include?(@prev_context.class)
# Render any configs which were added
new_configs = @prev_context.config.keys - @config_list.keys

Expand All @@ -95,7 +132,17 @@ def handle_context_change
end
end

if [HAProxy::Listener, HAProxy::Backend].include?(@prev_context.class)
if OPTION_NODES.include?(@prev_context.class)
# Render any configs which were added
new_options = @prev_context.options.keys - @option_list.keys

new_options.each do |option_name|
option_value = @prev_context.options[option_name]
render_option_line(option_name, option_value)
end
end

if SERVER_NODES.include?(@prev_context.class)
# Render any servers that were added
new_servers = @prev_context.servers.keys - @server_list.keys

Expand All @@ -105,6 +152,8 @@ def handle_context_change
end
end
@server_list = {}
@config_list = {}
@option_list = {}
end

def render_server_attributes(attributes)
Expand Down Expand Up @@ -137,6 +186,9 @@ def update_render_context(e)
when 'HAProxy::Treetop::BackendSection'
section_name = e.backend_header.proxy_name ? e.backend_header.proxy_name.content : nil
@context = @config.backend(section_name)
when 'HAProxy::Treetop::UserlistSection'
section_name = e.userlist_header.proxy_name ? e.userlist_header.proxy_name.content : nil
@context = @config.userlist(section_name)
else
@context = @prev_context
end
Expand Down
17 changes: 14 additions & 3 deletions lib/haproxy/treetop/config.treetop
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module HAProxy::Treetop
end

rule userlist_header
whitespace "userlist" whitespace proxy_name comment_text? line_break <UseristHeader>
whitespace "userlist" whitespace proxy_name comment_text? line_break <UserlistHeader>
end

rule defaults_header
Expand Down Expand Up @@ -68,7 +68,7 @@ module HAProxy::Treetop
end

rule config_line
whitespace !("defaults" / "global" / "listen" / "frontend" / "backend") keyword whitespace value? comment_text? line_break <ConfigLine>
whitespace !("defaults" / "global" / "listen" / "frontend" / "backend" / "userlist") keyword whitespace value? comment_text? line_break <ConfigLine>
end

rule comment_line
Expand All @@ -88,7 +88,18 @@ module HAProxy::Treetop
end

rule keyword
(("errorfile" / "timeout") whitespace)? [a-z0-9\-\.]+ <Keyword>
((
"capture response header" /
"capture request header" /
"use_backend" /
"errorfile" /
"errorloc" /
"timeout" /
"group" /
"stats" /
"user" /
"acl"
) whitespace)? [A-Za-z0-9\-\._]+ <Keyword>
end

rule server_name
Expand Down
12 changes: 12 additions & 0 deletions lib/haproxy/treetop/nodes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ def attribute
class OptionLine < ::Treetop::Runtime::SyntaxNode
include StrippedTextContent
include OptionalValueElement

def key
self.keyword.content
end

def attribute
self.value.content
end
end

class ServerLine < ::Treetop::Runtime::SyntaxNode
Expand Down Expand Up @@ -204,6 +212,10 @@ def frontends
self.elements.select {|e| e.class == FrontendSection}
end

def userlists
self.elements.select {|e| e.class == UserlistSection}
end

def backends
self.elements.select {|e| e.class == BackendSection}
end
Expand Down