haproxy-tools 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,189 @@
1
+ require 'haproxy/treetop/nodes'
2
+
3
+ module HAProxy::Treetop
4
+
5
+ grammar Config
6
+ rule configuration
7
+ (comment_line / global_section / defaults_section / userlist_section / listen_section / frontend_section / backend_section)* <ConfigurationFile>
8
+ end
9
+
10
+ rule global_section
11
+ global_header config_block <GlobalSection>
12
+ end
13
+
14
+ rule defaults_section
15
+ defaults_header config_block <DefaultsSection>
16
+ end
17
+
18
+ rule userlist_section
19
+ userlist_header config_block <UserlistSection>
20
+ end
21
+
22
+ rule listen_section
23
+ listen_header config_block <ListenSection>
24
+ end
25
+
26
+ rule frontend_section
27
+ frontend_header config_block <FrontendSection>
28
+ end
29
+
30
+ rule backend_section
31
+ backend_header config_block <BackendSection>
32
+ end
33
+
34
+ rule global_header
35
+ whitespace "global" whitespace comment_text? line_break <GlobalHeader>
36
+ end
37
+
38
+ rule userlist_header
39
+ whitespace "userlist" whitespace proxy_name comment_text? line_break <UseristHeader>
40
+ end
41
+
42
+ rule defaults_header
43
+ whitespace "defaults" whitespace proxy_name? whitespace comment_text? line_break <DefaultsHeader>
44
+ end
45
+
46
+ rule listen_header
47
+ whitespace "listen" whitespace proxy_name whitespace service_address? value? comment_text? line_break <ListenHeader>
48
+ end
49
+
50
+ rule frontend_header
51
+ whitespace "frontend" whitespace proxy_name whitespace service_address? value? comment_text? line_break <FrontendHeader>
52
+ end
53
+
54
+ rule backend_header
55
+ whitespace "backend" whitespace proxy_name whitespace value? comment_text? line_break <BackendHeader>
56
+ end
57
+
58
+ rule config_block
59
+ (server_line / option_line / config_line / comment_line / blank_line)* <ConfigBlock>
60
+ end
61
+
62
+ rule server_line
63
+ whitespace "server" whitespace server_name whitespace service_address value? comment_text? line_break <ServerLine>
64
+ end
65
+
66
+ rule option_line
67
+ whitespace "option" whitespace keyword whitespace value? comment_text? line_break <OptionLine>
68
+ end
69
+
70
+ rule config_line
71
+ whitespace !("defaults" / "global" / "listen" / "frontend" / "backend") keyword whitespace value? comment_text? line_break <ConfigLine>
72
+ end
73
+
74
+ rule comment_line
75
+ whitespace comment_text line_break <CommentLine>
76
+ end
77
+
78
+ rule blank_line
79
+ whitespace line_break <BlankLine>
80
+ end
81
+
82
+ rule comment_text
83
+ '#' char* &line_break <CommentText>
84
+ end
85
+
86
+ rule line_break
87
+ [\n] <LineBreak>
88
+ end
89
+
90
+ rule keyword
91
+ [a-z\-\.]+ <Keyword>
92
+ end
93
+
94
+ rule server_name
95
+ [a-zA-Z0-9\-_\.:]+ <ServerName>
96
+ end
97
+
98
+ rule service_address
99
+ host [:]? port <ServiceAddress>
100
+ end
101
+
102
+ rule host
103
+ ipv4_host / dns_host / wildcard_host
104
+ end
105
+
106
+ rule port
107
+ [\d]* <Port>
108
+ end
109
+
110
+ rule ipv4_host
111
+ [\d] 1..3 '.' [\d] 1..3 '.' [\d] 1..3 '.' [\d] 1..3 <Host>
112
+ end
113
+
114
+ rule wildcard_host
115
+ "*" <Host>
116
+ end
117
+
118
+ rule dns_host
119
+ [a-zA-Z\-\.] 4..255 <Host>
120
+ end
121
+
122
+ rule proxy_name
123
+ [a-zA-Z0-9\-_\.:]+ <ProxyName>
124
+ end
125
+
126
+ rule value
127
+ [^#\n]+ <Value>
128
+ end
129
+
130
+ rule char
131
+ ![\n] . <Char>
132
+ end
133
+
134
+ rule whitespace
135
+ [ \t]* <Whitespace>
136
+ end
137
+
138
+ # Valid Global Keywords (1.4)
139
+ # * Process management and security
140
+ # - chroot <jail dir>
141
+ # - daemon
142
+ # - gid <number>
143
+ # - group <group name>
144
+ # - log <address> <facility> [max level [min level]]
145
+ # - log-send-hostname [<string>]
146
+ # ? log-tag <string>
147
+ # - nbproc <number>
148
+ # - pidfile <pidfile>
149
+ # - uid <number>
150
+ # - ulimit-n <number>
151
+ # - user <user name>
152
+ # - stats socket <path> [{uid | user} <uid>] [{gid | group} <gid>] [mode <mode>] [level <level>]
153
+ # - stats timeout <timeout, in milliseconds>
154
+ # - stats maxconn <connections>
155
+ # - node <name>
156
+ # - description <text>
157
+ #
158
+ # * Performance tuning
159
+ # - maxconn <number>
160
+ # - maxpipes <number>
161
+ # - noepoll
162
+ # - nokqueue
163
+ # - nopoll
164
+ # - nosepoll
165
+ # - nosplice
166
+ # - spread-checks <0..50, in percent>
167
+ # - tune.bufsize <number>
168
+ # - tune.chksize <number>
169
+ # - tune.maxaccept <number>
170
+ # - tune.maxpollevents <number>
171
+ # - tune.maxrewrite <number>
172
+ # - tune.rcvbuf.client <number>
173
+ # - tune.rcvbuf.server <number>
174
+ # - tune.sndbuf.client <number>
175
+ # - tune.sndbuf.server <number>
176
+ #
177
+ # * Debugging
178
+ # - debug
179
+ # - quiet
180
+
181
+ # Valid Userlist keywords
182
+ # - userlist <listname>
183
+ # - group <groupname> [users <user>,<user>,(...)]
184
+ # - user <username> [password|insecure-password <password>] [groups <group>,<group>,(...)]
185
+
186
+
187
+ end
188
+ end
189
+
@@ -0,0 +1,174 @@
1
+ module HAProxy
2
+ module Treetop
3
+ extend self
4
+
5
+ module StrippedTextContent
6
+ def content
7
+ self.text_value.strip
8
+ end
9
+ end
10
+
11
+ module ConfigBlockContainer
12
+ def option_lines
13
+ self.config_block.elements.select {|e| e.class == OptionLine}
14
+ end
15
+
16
+ def config_lines
17
+ self.config_block.elements.select {|e| e.class == ConfigLine}
18
+ end
19
+ end
20
+
21
+ module ServiceAddressContainer
22
+ def service_address
23
+ self.elements.find {|e| e.class == ServiceAddress }
24
+ end
25
+
26
+ def host
27
+ self.service_address.host.text_value.strip
28
+ end
29
+
30
+ def port
31
+ self.service_address.port.text_value.strip
32
+ end
33
+ end
34
+
35
+ module ServerContainer
36
+ def servers
37
+ self.config_block.elements.select {|e| e.class == ServerLine}
38
+ end
39
+ end
40
+
41
+ module OptionalValueElement
42
+ def value
43
+ self.elements.find {|e| e.class == Value}
44
+ end
45
+ end
46
+
47
+ class Whitespace < ::Treetop::Runtime::SyntaxNode
48
+ def content
49
+ self.text_value
50
+ end
51
+ end
52
+ class LineBreak < ::Treetop::Runtime::SyntaxNode; end
53
+ class Char < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
54
+ class Keyword < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
55
+ class ProxyName < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
56
+ class ServerName < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
57
+ class Host < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
58
+ class Port < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
59
+ class Value < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
60
+ class CommentText < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
61
+
62
+ class ServiceAddress < ::Treetop::Runtime::SyntaxNode
63
+ include StrippedTextContent
64
+ end
65
+
66
+ class CommentLine < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
67
+ class BlankLine < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
68
+ class ConfigLine < ::Treetop::Runtime::SyntaxNode
69
+ include StrippedTextContent
70
+ include OptionalValueElement
71
+ end
72
+
73
+ class OptionLine < ::Treetop::Runtime::SyntaxNode
74
+ include StrippedTextContent
75
+ include OptionalValueElement
76
+ end
77
+
78
+ class ServerLine < ::Treetop::Runtime::SyntaxNode
79
+ include StrippedTextContent
80
+ include ServiceAddressContainer
81
+ include OptionalValueElement
82
+
83
+ def name
84
+ self.server_name.content
85
+ end
86
+ end
87
+
88
+ class GlobalHeader < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
89
+
90
+ class DefaultsHeader < ::Treetop::Runtime::SyntaxNode
91
+ include StrippedTextContent
92
+ def proxy_name
93
+ self.elements.select {|e| e.class == ProxyName}.first
94
+ end
95
+ end
96
+
97
+ class UserlistHeader < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
98
+ class BackendHeader < ::Treetop::Runtime::SyntaxNode; include StrippedTextContent; end
99
+
100
+ class FrontendHeader < ::Treetop::Runtime::SyntaxNode
101
+ include ServiceAddressContainer
102
+ end
103
+
104
+ class ListenHeader < ::Treetop::Runtime::SyntaxNode
105
+ include ServiceAddressContainer
106
+ end
107
+
108
+ class ConfigBlock < ::Treetop::Runtime::SyntaxNode; end
109
+
110
+ class DefaultsSection < ::Treetop::Runtime::SyntaxNode
111
+ include ConfigBlockContainer
112
+ end
113
+
114
+ class GlobalSection < ::Treetop::Runtime::SyntaxNode
115
+ include ConfigBlockContainer
116
+ end
117
+
118
+ class UserlistSection < ::Treetop::Runtime::SyntaxNode
119
+ include ConfigBlockContainer
120
+ end
121
+
122
+ class FrontendSection < ::Treetop::Runtime::SyntaxNode
123
+ include ConfigBlockContainer
124
+ end
125
+
126
+ class ListenSection < ::Treetop::Runtime::SyntaxNode
127
+ include ConfigBlockContainer
128
+ include ServerContainer
129
+ end
130
+
131
+ class BackendSection < ::Treetop::Runtime::SyntaxNode
132
+ include ConfigBlockContainer
133
+ include ServerContainer
134
+ end
135
+
136
+ class ConfigurationFile < ::Treetop::Runtime::SyntaxNode
137
+ def global
138
+ self.elements.select {|e| e.class == GlobalSection}.first
139
+ end
140
+
141
+ def defaults
142
+ self.elements.select {|e| e.class == DefaultsSection}
143
+ end
144
+
145
+ def listeners
146
+ self.elements.select {|e| e.class == ListenSection}
147
+ end
148
+
149
+ def frontends
150
+ self.elements.select {|e| e.class == FrontendSection}
151
+ end
152
+
153
+ def backends
154
+ self.elements.select {|e| e.class == BackendSection}
155
+ end
156
+ end
157
+
158
+ def print_node(e, depth, options = nil)
159
+ options ||= {}
160
+ options = {:max_depth => 2}.merge(options)
161
+
162
+ puts if depth == 0
163
+ print "--" * depth
164
+ print " #{e.class.name.split('::').last}"
165
+ print " [#{e.text_value}]" if e.class == ::Treetop::Runtime::SyntaxNode
166
+ print " [#{e.content}]" if e.respond_to? :content
167
+ puts
168
+ e.elements.each do |child|
169
+ print_node(child, depth + 1, options)
170
+ end if depth < options[:max_depth] && e.elements && !e.respond_to?(:content)
171
+ end
172
+ end
173
+ end
174
+
data/lib/haproxy_tools.rb CHANGED
@@ -1,2 +1,12 @@
1
+ require 'polyglot'
2
+ require 'treetop'
3
+ require 'orderedhash'
4
+ require 'haproxy/treetop/config'
1
5
  require 'haproxy/config'
6
+ require 'haproxy/renderer'
2
7
  require 'haproxy/parser'
8
+
9
+ module HAProxy
10
+ VERSION = File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).strip
11
+ end
12
+
@@ -1,3 +1,4 @@
1
+ # This is a top-level comment. It should be allowed.
1
2
  global
2
3
  maxconn 4096 # Total Max Connections. This is dependent on ulimit
3
4
  daemon
@@ -10,7 +11,7 @@ defaults
10
11
  contimeout 4000
11
12
  option httpclose # Disable Keepalive
12
13
 
13
- listen http_proxy 55.55.55.55:80
14
+ listen http_proxy 55.55.55.55:80
14
15
  balance roundrobin # Load Balancing algorithm
15
16
  option httpchk
16
17
  option forwardfor # This sets X-Forwarded-For
@@ -1,4 +1,103 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "HAProxy::Config" do
4
+ describe 'render multi-backend config' do
5
+ before(:each) do
6
+ @config = HAProxy::Config.parse_file('spec/fixtures/multi-pool.haproxy.cfg')
7
+ end
8
+
9
+ it 'can re-render a config file with a server removed' do
10
+ l = @config.backend('www_main')
11
+ l.servers.delete('prd_www_1')
12
+
13
+ new_config_text = @config.render
14
+
15
+ new_config = HAProxy::Parser.new.parse(new_config_text)
16
+ new_config.backend('www_main').servers['prd_www_1'].should be_nil
17
+ end
18
+
19
+ it 'can re-render a config file with a server attribute added' do
20
+ b = @config.backend('www_main')
21
+ b.servers['prd_www_1'].attributes['disabled'] = true
22
+ new_config_text = @config.render
23
+
24
+ new_config = HAProxy::Parser.new.parse(new_config_text)
25
+ s = new_config.backend('www_main').servers['prd_www_1']
26
+ s.should_not be_nil
27
+ s.attributes['disabled'].should be_true
28
+ end
29
+
30
+ it 'can re-render a config file with a server added' do
31
+ b = @config.backend('www_main')
32
+ b.add_server('prd_www_4', '99.99.99.99', :port => '8000')
33
+
34
+ new_config_text = @config.render
35
+
36
+ new_config = HAProxy::Parser.new.parse(new_config_text)
37
+ s = new_config.backend('www_main').servers['prd_www_4']
38
+ s.should_not be_nil
39
+ s.name.should == 'prd_www_4'
40
+ s.host.should == '99.99.99.99'
41
+ s.port.should == '8000'
42
+ s.attributes.to_a.should == []
43
+ end
44
+
45
+ it 'can re-render a config file with a server added based on template' do
46
+ b = @config.backend('www_main')
47
+ b.add_server('prd_www_4', '99.99.99.99', :template => b.servers['prd_www_1'])
48
+
49
+ new_config_text = @config.render
50
+
51
+ new_config = HAProxy::Parser.new.parse(new_config_text)
52
+ s = new_config.backend('www_main').servers['prd_www_4']
53
+ s.should_not be_nil
54
+ s.name.should == 'prd_www_4'
55
+ s.host.should == '99.99.99.99'
56
+ s.port.should == '8000'
57
+ s.attributes.to_a.should == [
58
+ ['cookie','i-prd_www_1'],
59
+ ['check',true],
60
+ ['inter','3000'],
61
+ ['rise','2'],
62
+ ['fall','3'],
63
+ ['maxconn','1000']
64
+ ]
65
+ end
66
+ end
67
+
68
+ describe 'render simple config' do
69
+ before(:each) do
70
+ @config = HAProxy::Config.parse_file('spec/fixtures/simple.haproxy.cfg')
71
+ end
72
+
73
+ it 'can re-render a config file with a server removed' do
74
+ l = @config.listener('http_proxy')
75
+ l.servers.delete('web1')
76
+
77
+ new_config_text = @config.render
78
+
79
+ new_config = HAProxy::Parser.new.parse(new_config_text)
80
+ new_config.listener('http_proxy').servers['web1'].should be_nil
81
+ end
82
+
83
+ it 'can re-render a config file with a server added' do
84
+ l = @config.listener('http_proxy')
85
+ l.add_server('web4', '99.99.99.99', :template => l.servers['web1'])
86
+
87
+ new_config_text = @config.render
88
+
89
+ new_config = HAProxy::Parser.new.parse(new_config_text)
90
+ s = new_config.listener('http_proxy').servers['web4']
91
+ s.should_not be_nil
92
+ s.name.should == 'web4'
93
+ s.host.should == '99.99.99.99'
94
+ s.port.should == '80'
95
+ s.attributes.to_a.should == [
96
+ ['weight','1'],
97
+ ['maxconn','512'],
98
+ ['check',true]
99
+ ]
100
+ end
101
+ end
4
102
  end
103
+