class HAProxy::Parser

Responsible for reading an HAProxy config file and building an HAProxy::Config instance.

Constants

Error

Raised when an error occurs during parsing.

SERVER_ATTRIBUTE_NAMES
SERVER_ATTRIBUTE_NAMES_1_3

haproxy 1.3

SERVER_ATTRIBUTE_NAMES_1_4

Added in haproxy 1.4

SERVER_ATTRIBUTE_NAMES_1_5

Added in haproxy 1.5

SERVER_ATTRIBUTE_NAMES_1_6

Added in haproxy 1.6

SERVER_ATTRIBUTE_NAMES_1_7

Added in haproxy 1.7

SERVER_ATTRIBUTE_NAMES_1_8
SERVER_ATTRIBUTE_NAMES_1_9

Attributes

options[RW]
parse_result[RW]
verbose[RW]

Public Class Methods

new(options = nil) click to toggle source
# File lib/haproxy/parser.rb, line 52
def initialize(options = nil)
  options ||= {}
  options = {verbose: false}.merge(options)

  self.options = options
  self.verbose = options[:verbose]
end

Public Instance Methods

parse(config_text) click to toggle source
# File lib/haproxy/parser.rb, line 65
def parse(config_text)
  result = parse_config_text(config_text)
  self.parse_result = result
  build_config(result)
end
parse_file(filename) click to toggle source
# File lib/haproxy/parser.rb, line 60
def parse_file(filename)
  config_text = File.read(filename)
  parse(config_text)
end

Protected Instance Methods

build_backend(bs) click to toggle source
# File lib/haproxy/parser.rb, line 101
def build_backend(bs)
  Backend.new.tap do |b|
    b.name        = try_send(bs.backend_header, :proxy_name, :content)
    b.options     = options_hash_from_config_section(bs)
    b.config      = config_hash_from_config_section(bs)
    b.servers     = server_hash_from_config_section(bs)
  end
end
build_config(result) click to toggle source
# File lib/haproxy/parser.rb, line 80
def build_config(result)
  HAProxy::Config.new(result).tap do |config|
    config.global = config_hash_from_config_section(result.global)

    config.frontends  += collect_frontends(result)
    config.backends   += collect_backends(result)
    config.listeners  += collect_listeners(result)
    config.defaults   += collect_defaults(result)
  end
end
build_default(ds) click to toggle source
# File lib/haproxy/parser.rb, line 121
def build_default(ds)
  Default.new.tap do |d|
    d.name        = try_send(ds.defaults_header, :proxy_name, :content)
    d.options     = options_hash_from_config_section(ds)
    d.config      = config_hash_from_config_section(ds)
  end
end
build_frontend(fs) click to toggle source
# File lib/haproxy/parser.rb, line 91
def build_frontend(fs)
  Frontend.new.tap do |f|
    f.name        = try_send(fs.frontend_header, :proxy_name, :content)
    f.host        = try_send(fs.frontend_header, :service_address, :host, :content)
    f.port        = try_send(fs.frontend_header, :service_address, :port, :content)
    f.options     = options_hash_from_config_section(fs)
    f.config      = config_hash_from_config_section(fs)
  end
end
build_listener(ls) click to toggle source
# File lib/haproxy/parser.rb, line 110
def build_listener(ls)
  Listener.new.tap do |l|
    l.name        = try_send(ls.listen_header, :proxy_name, :content)
    l.host        = try_send(ls.listen_header, :service_address, :host, :content)
    l.port        = try_send(ls.listen_header, :service_address, :port, :content)
    l.options     = options_hash_from_config_section(ls)
    l.config      = config_hash_from_config_section(ls)
    l.servers     = server_hash_from_config_section(ls)
  end
end
clean_parsed_server_attributes(pairs) click to toggle source

Converts attributes with no values to true, and combines everything else into space- separated strings.

# File lib/haproxy/parser.rb, line 187
def clean_parsed_server_attributes(pairs)
  pairs.each do |k, v|
    pairs[k] = if v.empty?
      true
    else
      v.join(" ")
    end
  end
end
collect_backends(result) click to toggle source
# File lib/haproxy/parser.rb, line 133
def collect_backends(result)
  result.backends.map { |bs| build_backend(bs) }
end
collect_defaults(result) click to toggle source
# File lib/haproxy/parser.rb, line 141
def collect_defaults(result)
  result.defaults.map { |ds| build_default(ds) }
end
collect_frontends(result) click to toggle source
# File lib/haproxy/parser.rb, line 129
def collect_frontends(result)
  result.frontends.map { |fs| build_frontend(fs) }
end
collect_listeners(result) click to toggle source
# File lib/haproxy/parser.rb, line 137
def collect_listeners(result)
  result.listeners.map { |ls| build_listener(ls) }
end
config_hash_from_config_section(cs) click to toggle source
# File lib/haproxy/parser.rb, line 203
def config_hash_from_config_section(cs)
  cs.config_lines.reject {|l| l.keyword.content == "option"}.each_with_object({}) do |l, ch|
    ch[l.keyword.content] = l.value ? l.value.content : nil
  end
end
options_hash_from_config_section(cs) click to toggle source
# File lib/haproxy/parser.rb, line 197
def options_hash_from_config_section(cs)
  cs.option_lines.each_with_object({}) do |l, ch|
    ch[l.keyword.content] = l.value ? l.value.content : nil
  end
end
parse_config_text(config_text) click to toggle source
# File lib/haproxy/parser.rb, line 73
def parse_config_text(config_text)
  parser = HAProxy::Treetop::ConfigParser.new
  result = parser.parse(config_text)
  raise HAProxy::Parser::Error.new(parser.failure_reason) if result.nil?
  result
end
parse_server_attributes(value) click to toggle source

Parses server attributes from the server value. I couldn't manage to get treetop to do this.

Types of server attributes to support: ipv4, boolean, string, integer, time (us, ms, s, m, h, d), url, source attributes

BUG: If an attribute value matches an attribute name, the parser will assume that a new attribute value has started. I don't know how haproxy itself handles that situation.

# File lib/haproxy/parser.rb, line 168
def parse_server_attributes(value)
  parts = value.to_s.split(/\s/)
  current_name = nil
  pairs = parts.each_with_object({}) { |part, attrs|
    if SERVER_ATTRIBUTE_NAMES.include?(part)
      current_name = part
      attrs[current_name] = []
    elsif current_name.nil?
      raise "Invalid server attribute: #{part}"
    else
      attrs[current_name] << part
    end
  }

  clean_parsed_server_attributes(pairs)
end
server_hash_from_config_section(cs) click to toggle source
# File lib/haproxy/parser.rb, line 153
def server_hash_from_config_section(cs)
  cs.servers.each_with_object({}) do |s, ch|
    value = try_send(s, :value, :content)
    ch[s.name] = Server.new(s.name, s.host, s.port, parse_server_attributes(value))
  end
end
try_send(node, *method_names) click to toggle source
# File lib/haproxy/parser.rb, line 145
def try_send(node, *method_names)
  method_name = method_names.shift
  if node.respond_to?(method_name)
    next_node = node.send(method_name)
    method_names.empty? ? next_node : try_send(next_node, *method_names)
  end
end