synapse 0.2.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/.gitignore +3 -0
  2. data/.mailmap +3 -0
  3. data/LICENSE.txt +2 -2
  4. data/Makefile +6 -0
  5. data/README.md +42 -13
  6. data/bin/synapse +29 -21
  7. data/config/hostheader_test.json +71 -0
  8. data/config/svcdir_test.json +46 -0
  9. data/config/synapse.conf.json +26 -32
  10. data/config/synapse_services/service1.json +24 -0
  11. data/config/synapse_services/service2.json +24 -0
  12. data/lib/synapse.rb +39 -24
  13. data/lib/synapse/base.rb +1 -1
  14. data/lib/synapse/haproxy.rb +579 -22
  15. data/lib/synapse/log.rb +24 -0
  16. data/lib/synapse/service_watcher.rb +10 -6
  17. data/lib/synapse/service_watcher/base.rb +33 -11
  18. data/lib/synapse/service_watcher/dns.rb +28 -20
  19. data/lib/synapse/service_watcher/docker.rb +108 -0
  20. data/lib/synapse/service_watcher/ec2tag.rb +1 -1
  21. data/lib/synapse/service_watcher/zookeeper.rb +25 -28
  22. data/lib/synapse/version.rb +1 -1
  23. data/spec/spec_helper.rb +2 -2
  24. data/synapse.gemspec +2 -3
  25. metadata +15 -25
  26. data/Vagrantfile +0 -112
  27. data/chef/converge +0 -4
  28. data/chef/cookbooks/lxc/recipes/default.rb +0 -2
  29. data/chef/cookbooks/synapse/attributes/default.rb +0 -1
  30. data/chef/cookbooks/synapse/recipes/default.rb +0 -6
  31. data/chef/run.json +0 -8
  32. data/chef/run.rb +0 -2
  33. data/client/.RData +0 -0
  34. data/client/.Rhistory +0 -294
  35. data/client/bench_rewrite_config.dat +0 -2013
  36. data/client/benchmark-client.iml +0 -20
  37. data/client/pom.xml +0 -45
  38. data/client/src/main/java/ClientArsch.java +0 -68
  39. data/client/src/main/java/META-INF/MANIFEST.MF +0 -3
  40. data/haproxy.pid +0 -1
  41. data/lib/gen-rb/endpoint_types.rb +0 -65
  42. data/lib/gen-rb/thrift.rb +0 -65
  43. data/test.sh +0 -3
@@ -0,0 +1,24 @@
1
+ {
2
+ "default_servers": [
3
+ { "name": "default1", "host": "localhost", "port": 8080 }
4
+ ],
5
+ "discovery": {
6
+ "method": "dns",
7
+ "nameserver": "10.10.1.13",
8
+ "servers": [
9
+ "0.www.example.com",
10
+ "1.www.example.com"
11
+ ]
12
+ },
13
+ "haproxy": {
14
+ "server_options": "check inter 2s rise 3 fall 2",
15
+ "listen": [
16
+ "mode http",
17
+ "option httplog"
18
+ ],
19
+ "backend": [
20
+ "mode http",
21
+ "option httpchk GET /health HTTP/1.0"
22
+ ]
23
+ }
24
+ }
data/lib/synapse.rb CHANGED
@@ -1,7 +1,8 @@
1
- require_relative "synapse/version"
2
- require_relative "synapse/base"
3
- require_relative "synapse/haproxy"
4
- require_relative "synapse/service_watcher"
1
+ require "synapse/version"
2
+ require "synapse/service_watcher/base"
3
+ require "synapse/haproxy"
4
+ require "synapse/service_watcher"
5
+ require "synapse/log"
5
6
 
6
7
  require 'logger'
7
8
  require 'json'
@@ -10,10 +11,8 @@ include Synapse
10
11
 
11
12
  module Synapse
12
13
  class Synapse
14
+ include Logging
13
15
  def initialize(opts={})
14
- # disable configuration until this is started
15
- @configure_enabled = false
16
-
17
16
  # create the service watchers for all our services
18
17
  raise "specify a list of services to connect in the config" unless opts.has_key?('services')
19
18
  @service_watchers = create_service_watchers(opts['services'])
@@ -21,44 +20,60 @@ module Synapse
21
20
  # create the haproxy object
22
21
  raise "haproxy config section is missing" unless opts.has_key?('haproxy')
23
22
  @haproxy = Haproxy.new(opts['haproxy'])
23
+
24
+ # configuration is initially enabled to configure on first loop
25
+ @config_updated = true
24
26
  end
25
27
 
26
28
  # start all the watchers and enable haproxy configuration
27
29
  def run
28
30
  log.info "synapse: starting..."
29
31
 
32
+ # start all the watchers
30
33
  @service_watchers.map { |watcher| watcher.start }
31
- @configure_enabled = true
32
- configure
33
34
 
34
- # loop forever
35
+ # main loop
35
36
  loops = 0
36
- loop do
37
- sleep 1
37
+ loop do
38
+ @service_watchers.each do |w|
39
+ raise "synapse: service watcher #{w.name} failed ping!" unless w.ping?
40
+ end
41
+
42
+ if @config_updated
43
+ @config_updated = false
44
+ log.info "synapse: regenerating haproxy config"
45
+ @haproxy.update_config(@service_watchers)
46
+ else
47
+ sleep 1
48
+ end
49
+
38
50
  loops += 1
39
51
  log.debug "synapse: still running at #{Time.now}" if (loops % 60) == 0
40
52
  end
53
+
54
+ rescue StandardError => e
55
+ log.error "synapse: encountered unexpected exception #{e.inspect} in main thread"
56
+ raise e
57
+ ensure
58
+ log.warn "synapse: exiting; sending stop signal to all watchers"
59
+
60
+ # stop all the watchers
61
+ @service_watchers.map(&:stop)
41
62
  end
42
63
 
43
- # reconfigure haproxy based on our watchers
44
- def configure
45
- if @configure_enabled
46
- log.info "synapse: regenerating haproxy config"
47
- @haproxy.update_config(@service_watchers)
48
- else
49
- log.info "synapse: reconfigure requested, but it's not yet enabled"
50
- end
64
+ def reconfigure!
65
+ @config_updated = true
51
66
  end
52
67
 
53
68
  private
54
69
  def create_service_watchers(services={})
55
70
  service_watchers =[]
56
- services.each do |service_config|
57
- service_watchers << ServiceWatcher.create(service_config, self)
71
+ services.each do |service_name, service_config|
72
+ service_watchers << ServiceWatcher.create(service_name, service_config, self)
58
73
  end
59
-
74
+
60
75
  return service_watchers
61
76
  end
62
-
77
+
63
78
  end
64
79
  end
data/lib/synapse/base.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Synapse
2
2
  def log
3
- @@log ||= Logger.new(STDOUT)
3
+ @@log ||= Logger.new(STDERR)
4
4
  end
5
5
  end
@@ -1,8 +1,500 @@
1
+ require 'synapse/log'
2
+
1
3
  require 'socket'
4
+ require 'digest'
2
5
 
3
6
  module Synapse
4
7
  class Haproxy
8
+ include Logging
5
9
  attr_reader :opts
10
+
11
+ # these come from the documentation for haproxy 1.5
12
+ # http://haproxy.1wt.eu/download/1.5/doc/configuration.txt
13
+ @@section_fields = {
14
+ "backend" => [
15
+ "acl",
16
+ "appsession",
17
+ "balance",
18
+ "bind-process",
19
+ "block",
20
+ "compression",
21
+ "contimeout",
22
+ "cookie",
23
+ "default-server",
24
+ "description",
25
+ "disabled",
26
+ "dispatch",
27
+ "enabled",
28
+ "errorfile",
29
+ "errorloc",
30
+ "errorloc302",
31
+ "errorloc303",
32
+ "force-persist",
33
+ "fullconn",
34
+ "grace",
35
+ "hash-type",
36
+ "http-check disable-on-404",
37
+ "http-check expect",
38
+ "http-check send-state",
39
+ "http-request",
40
+ "http-response",
41
+ "id",
42
+ "ignore-persist",
43
+ "log",
44
+ "mode",
45
+ "option abortonclose",
46
+ "option accept-invalid-http-response",
47
+ "option allbackups",
48
+ "option checkcache",
49
+ "option forceclose",
50
+ "option forwardfor",
51
+ "option http-no-delay",
52
+ "option http-pretend-keepalive",
53
+ "option http-server-close",
54
+ "option httpchk",
55
+ "option httpclose",
56
+ "option httplog",
57
+ "option http_proxy",
58
+ "option independent-streams",
59
+ "option lb-agent-chk",
60
+ "option ldap-check",
61
+ "option log-health-checks",
62
+ "option mysql-check",
63
+ "option pgsql-check",
64
+ "option nolinger",
65
+ "option originalto",
66
+ "option persist",
67
+ "option redispatch",
68
+ "option redis-check",
69
+ "option smtpchk",
70
+ "option splice-auto",
71
+ "option splice-request",
72
+ "option splice-response",
73
+ "option srvtcpka",
74
+ "option ssl-hello-chk",
75
+ "option tcp-smart-connect",
76
+ "option tcpka",
77
+ "option tcplog",
78
+ "option transparent",
79
+ "persist rdp-cookie",
80
+ "redirect",
81
+ "redisp",
82
+ "redispatch",
83
+ "reqadd",
84
+ "reqallow",
85
+ "reqdel",
86
+ "reqdeny",
87
+ "reqiallow",
88
+ "reqidel",
89
+ "reqideny",
90
+ "reqipass",
91
+ "reqirep",
92
+ "reqisetbe",
93
+ "reqitarpit",
94
+ "reqpass",
95
+ "reqrep",
96
+ "reqsetbe",
97
+ "reqtarpit",
98
+ "retries",
99
+ "rspadd",
100
+ "rspdel",
101
+ "rspdeny",
102
+ "rspidel",
103
+ "rspideny",
104
+ "rspirep",
105
+ "rsprep",
106
+ "server",
107
+ "source",
108
+ "srvtimeout",
109
+ "stats admin",
110
+ "stats auth",
111
+ "stats enable",
112
+ "stats hide-version",
113
+ "stats http-request",
114
+ "stats realm",
115
+ "stats refresh",
116
+ "stats scope",
117
+ "stats show-desc",
118
+ "stats show-legends",
119
+ "stats show-node",
120
+ "stats uri",
121
+ "stick match",
122
+ "stick on",
123
+ "stick store-request",
124
+ "stick store-response",
125
+ "stick-table",
126
+ "tcp-request content",
127
+ "tcp-request inspect-delay",
128
+ "tcp-response content",
129
+ "tcp-response inspect-delay",
130
+ "timeout check",
131
+ "timeout connect",
132
+ "timeout contimeout",
133
+ "timeout http-keep-alive",
134
+ "timeout http-request",
135
+ "timeout queue",
136
+ "timeout server",
137
+ "timeout srvtimeout",
138
+ "timeout tarpit",
139
+ "timeout tunnel",
140
+ "transparent",
141
+ "use-server"
142
+ ],
143
+ "defaults" => [
144
+ "backlog",
145
+ "balance",
146
+ "bind-process",
147
+ "clitimeout",
148
+ "compression",
149
+ "contimeout",
150
+ "cookie",
151
+ "default-server",
152
+ "default_backend",
153
+ "disabled",
154
+ "enabled",
155
+ "errorfile",
156
+ "errorloc",
157
+ "errorloc302",
158
+ "errorloc303",
159
+ "fullconn",
160
+ "grace",
161
+ "hash-type",
162
+ "http-check disable-on-404",
163
+ "http-check send-state",
164
+ "log",
165
+ "maxconn",
166
+ "mode",
167
+ "monitor-net",
168
+ "monitor-uri",
169
+ "option abortonclose",
170
+ "option accept-invalid-http-request",
171
+ "option accept-invalid-http-response",
172
+ "option allbackups",
173
+ "option checkcache",
174
+ "option clitcpka",
175
+ "option contstats",
176
+ "option dontlog-normal",
177
+ "option dontlognull",
178
+ "option forceclose",
179
+ "option forwardfor",
180
+ "option http-no-delay",
181
+ "option http-pretend-keepalive",
182
+ "option http-server-close",
183
+ "option http-use-proxy-header",
184
+ "option httpchk",
185
+ "option httpclose",
186
+ "option httplog",
187
+ "option http_proxy",
188
+ "option independent-streams",
189
+ "option lb-agent-chk",
190
+ "option ldap-check",
191
+ "option log-health-checks",
192
+ "option log-separate-errors",
193
+ "option logasap",
194
+ "option mysql-check",
195
+ "option pgsql-check",
196
+ "option nolinger",
197
+ "option originalto",
198
+ "option persist",
199
+ "option redispatch",
200
+ "option redis-check",
201
+ "option smtpchk",
202
+ "option socket-stats",
203
+ "option splice-auto",
204
+ "option splice-request",
205
+ "option splice-response",
206
+ "option srvtcpka",
207
+ "option ssl-hello-chk",
208
+ "option tcp-smart-accept",
209
+ "option tcp-smart-connect",
210
+ "option tcpka",
211
+ "option tcplog",
212
+ "option transparent",
213
+ "persist rdp-cookie",
214
+ "rate-limit sessions",
215
+ "redisp",
216
+ "redispatch",
217
+ "retries",
218
+ "source",
219
+ "srvtimeout",
220
+ "stats auth",
221
+ "stats enable",
222
+ "stats hide-version",
223
+ "stats realm",
224
+ "stats refresh",
225
+ "stats scope",
226
+ "stats show-desc",
227
+ "stats show-legends",
228
+ "stats show-node",
229
+ "stats uri",
230
+ "timeout check",
231
+ "timeout client",
232
+ "timeout clitimeout",
233
+ "timeout connect",
234
+ "timeout contimeout",
235
+ "timeout http-keep-alive",
236
+ "timeout http-request",
237
+ "timeout queue",
238
+ "timeout server",
239
+ "timeout srvtimeout",
240
+ "timeout tarpit",
241
+ "timeout tunnel",
242
+ "transparent",
243
+ "unique-id-format",
244
+ "unique-id-header"
245
+ ],
246
+ "frontend" => [
247
+ "acl",
248
+ "backlog",
249
+ "bind",
250
+ "bind-process",
251
+ "block",
252
+ "capture cookie",
253
+ "capture request header",
254
+ "capture response header",
255
+ "clitimeout",
256
+ "compression",
257
+ "default_backend",
258
+ "description",
259
+ "disabled",
260
+ "enabled",
261
+ "errorfile",
262
+ "errorloc",
263
+ "errorloc302",
264
+ "errorloc303",
265
+ "force-persist",
266
+ "grace",
267
+ "http-request",
268
+ "http-response",
269
+ "id",
270
+ "ignore-persist",
271
+ "log",
272
+ "maxconn",
273
+ "mode",
274
+ "monitor fail",
275
+ "monitor-net",
276
+ "monitor-uri",
277
+ "option accept-invalid-http-request",
278
+ "option clitcpka",
279
+ "option contstats",
280
+ "option dontlog-normal",
281
+ "option dontlognull",
282
+ "option forceclose",
283
+ "option forwardfor",
284
+ "option http-no-delay",
285
+ "option http-pretend-keepalive",
286
+ "option http-server-close",
287
+ "option http-use-proxy-header",
288
+ "option httpclose",
289
+ "option httplog",
290
+ "option http_proxy",
291
+ "option independent-streams",
292
+ "option log-separate-errors",
293
+ "option logasap",
294
+ "option nolinger",
295
+ "option originalto",
296
+ "option socket-stats",
297
+ "option splice-auto",
298
+ "option splice-request",
299
+ "option splice-response",
300
+ "option tcp-smart-accept",
301
+ "option tcpka",
302
+ "option tcplog",
303
+ "rate-limit sessions",
304
+ "redirect",
305
+ "reqadd",
306
+ "reqallow",
307
+ "reqdel",
308
+ "reqdeny",
309
+ "reqiallow",
310
+ "reqidel",
311
+ "reqideny",
312
+ "reqipass",
313
+ "reqirep",
314
+ "reqisetbe",
315
+ "reqitarpit",
316
+ "reqpass",
317
+ "reqrep",
318
+ "reqsetbe",
319
+ "reqtarpit",
320
+ "rspadd",
321
+ "rspdel",
322
+ "rspdeny",
323
+ "rspidel",
324
+ "rspideny",
325
+ "rspirep",
326
+ "rsprep",
327
+ "tcp-request connection",
328
+ "tcp-request content",
329
+ "tcp-request inspect-delay",
330
+ "timeout client",
331
+ "timeout clitimeout",
332
+ "timeout http-keep-alive",
333
+ "timeout http-request",
334
+ "timeout tarpit",
335
+ "unique-id-format",
336
+ "unique-id-header",
337
+ "use_backend"
338
+ ],
339
+ "listen" => [
340
+ "acl",
341
+ "appsession",
342
+ "backlog",
343
+ "balance",
344
+ "bind",
345
+ "bind-process",
346
+ "block",
347
+ "capture cookie",
348
+ "capture request header",
349
+ "capture response header",
350
+ "clitimeout",
351
+ "compression",
352
+ "contimeout",
353
+ "cookie",
354
+ "default-server",
355
+ "default_backend",
356
+ "description",
357
+ "disabled",
358
+ "dispatch",
359
+ "enabled",
360
+ "errorfile",
361
+ "errorloc",
362
+ "errorloc302",
363
+ "errorloc303",
364
+ "force-persist",
365
+ "fullconn",
366
+ "grace",
367
+ "hash-type",
368
+ "http-check disable-on-404",
369
+ "http-check expect",
370
+ "http-check send-state",
371
+ "http-request",
372
+ "http-response",
373
+ "id",
374
+ "ignore-persist",
375
+ "log",
376
+ "maxconn",
377
+ "mode",
378
+ "monitor fail",
379
+ "monitor-net",
380
+ "monitor-uri",
381
+ "option abortonclose",
382
+ "option accept-invalid-http-request",
383
+ "option accept-invalid-http-response",
384
+ "option allbackups",
385
+ "option checkcache",
386
+ "option clitcpka",
387
+ "option contstats",
388
+ "option dontlog-normal",
389
+ "option dontlognull",
390
+ "option forceclose",
391
+ "option forwardfor",
392
+ "option http-no-delay",
393
+ "option http-pretend-keepalive",
394
+ "option http-server-close",
395
+ "option http-use-proxy-header",
396
+ "option httpchk",
397
+ "option httpclose",
398
+ "option httplog",
399
+ "option http_proxy",
400
+ "option independent-streams",
401
+ "option lb-agent-chk",
402
+ "option ldap-check",
403
+ "option log-health-checks",
404
+ "option log-separate-errors",
405
+ "option logasap",
406
+ "option mysql-check",
407
+ "option pgsql-check",
408
+ "option nolinger",
409
+ "option originalto",
410
+ "option persist",
411
+ "option redispatch",
412
+ "option redis-check",
413
+ "option smtpchk",
414
+ "option socket-stats",
415
+ "option splice-auto",
416
+ "option splice-request",
417
+ "option splice-response",
418
+ "option srvtcpka",
419
+ "option ssl-hello-chk",
420
+ "option tcp-smart-accept",
421
+ "option tcp-smart-connect",
422
+ "option tcpka",
423
+ "option tcplog",
424
+ "option transparent",
425
+ "persist rdp-cookie",
426
+ "rate-limit sessions",
427
+ "redirect",
428
+ "redisp",
429
+ "redispatch",
430
+ "reqadd",
431
+ "reqallow",
432
+ "reqdel",
433
+ "reqdeny",
434
+ "reqiallow",
435
+ "reqidel",
436
+ "reqideny",
437
+ "reqipass",
438
+ "reqirep",
439
+ "reqisetbe",
440
+ "reqitarpit",
441
+ "reqpass",
442
+ "reqrep",
443
+ "reqsetbe",
444
+ "reqtarpit",
445
+ "retries",
446
+ "rspadd",
447
+ "rspdel",
448
+ "rspdeny",
449
+ "rspidel",
450
+ "rspideny",
451
+ "rspirep",
452
+ "rsprep",
453
+ "server",
454
+ "source",
455
+ "srvtimeout",
456
+ "stats admin",
457
+ "stats auth",
458
+ "stats enable",
459
+ "stats hide-version",
460
+ "stats http-request",
461
+ "stats realm",
462
+ "stats refresh",
463
+ "stats scope",
464
+ "stats show-desc",
465
+ "stats show-legends",
466
+ "stats show-node",
467
+ "stats uri",
468
+ "stick match",
469
+ "stick on",
470
+ "stick store-request",
471
+ "stick store-response",
472
+ "stick-table",
473
+ "tcp-request connection",
474
+ "tcp-request content",
475
+ "tcp-request inspect-delay",
476
+ "tcp-response content",
477
+ "tcp-response inspect-delay",
478
+ "timeout check",
479
+ "timeout client",
480
+ "timeout clitimeout",
481
+ "timeout connect",
482
+ "timeout contimeout",
483
+ "timeout http-keep-alive",
484
+ "timeout http-request",
485
+ "timeout queue",
486
+ "timeout server",
487
+ "timeout srvtimeout",
488
+ "timeout tarpit",
489
+ "timeout tunnel",
490
+ "transparent",
491
+ "unique-id-format",
492
+ "unique-id-header",
493
+ "use_backend",
494
+ "use-server"
495
+ ]
496
+ }
497
+
6
498
  def initialize(opts)
7
499
  super()
8
500
 
@@ -22,7 +514,14 @@ module Synapse
22
514
  end
23
515
 
24
516
  @opts = opts
517
+
518
+ # how to restart haproxy
519
+ @restart_interval = 2
25
520
  @restart_required = true
521
+ @last_restart = Time.new(0)
522
+
523
+ # a place to store the parsed haproxy config from each watcher
524
+ @watcher_configs = {}
26
525
  end
27
526
 
28
527
  def update_config(watchers)
@@ -45,29 +544,35 @@ module Synapse
45
544
 
46
545
  # generates a new config based on the state of the watchers
47
546
  def generate_config(watchers)
48
- new_config = generate_base_config + "\n"
49
- new_config << watchers.map {|w| generate_listen_stanza(w)}.join("\n")
547
+ new_config = generate_base_config
548
+
549
+ watchers.each do |watcher|
550
+ @watcher_configs[watcher.name] ||= parse_watcher_config(watcher)
551
+
552
+ new_config << generate_frontend_stanza(watcher, @watcher_configs[watcher.name]['frontend'])
553
+ new_config << generate_backend_stanza(watcher, @watcher_configs[watcher.name]['backend'])
554
+ end
50
555
 
51
556
  log.debug "synapse: new haproxy config: #{new_config}"
52
- return new_config
557
+ return new_config.flatten.join("\n")
53
558
  end
54
559
 
55
560
  # generates the global and defaults sections of the config file
56
561
  def generate_base_config
57
- base_config = "# auto-generated by synapse at #{Time.now}\n"
562
+ base_config = ["# auto-generated by synapse at #{Time.now}\n"]
58
563
 
59
564
  %w{global defaults}.each do |section|
60
- base_config << "\n#{section}\n"
565
+ base_config << "#{section}"
61
566
  @opts[section].each do |option|
62
- base_config << "\t#{option}\n"
567
+ base_config << "\t#{option}"
63
568
  end
64
569
  end
65
570
 
66
571
  if @opts['extra_sections']
67
572
  @opts['extra_sections'].each do |title, section|
68
- base_config << "\n#{title}\n"
573
+ base_config << "\n#{title}"
69
574
  section.each do |option|
70
- base_config << "\t#{option}\n"
575
+ base_config << "\t#{option}"
71
576
  end
72
577
  end
73
578
  end
@@ -75,24 +580,62 @@ module Synapse
75
580
  return base_config
76
581
  end
77
582
 
78
- # generates an individual stanza for a particular watcher
79
- def generate_listen_stanza(watcher)
80
- if watcher.backends.empty?
81
- log.warn "synapse: no backends found for watcher #{watcher.name}"
82
- return ""
583
+ # split the haproxy config in each watcher into fields applicable in
584
+ # frontend and backend sections
585
+ def parse_watcher_config(watcher)
586
+ config = {}
587
+ %w{frontend backend}.each do |section|
588
+ config[section] = watcher.haproxy[section] || []
589
+
590
+ # copy over the settings from the 'listen' section that pertain to section
591
+ config[section].concat(
592
+ watcher.haproxy['listen'].select {|setting|
593
+ parsed_setting = setting.strip.gsub(/\s+/, ' ').downcase
594
+ @@section_fields[section].any? {|field| parsed_setting.start_with?(field)}
595
+ })
596
+
597
+ # pick only those fields that are valid and warn about the invalid ones
598
+ config[section].select!{|setting|
599
+ parsed_setting = setting.strip.gsub(/\s+/, ' ').downcase
600
+ if @@section_fields[section].any? {|field| parsed_setting.start_with?(field)}
601
+ true
602
+ else
603
+ log.warn "synapse: service #{watcher.name} contains invalid #{section} setting: '#{setting}'"
604
+ false
605
+ end
606
+ }
83
607
  end
84
608
 
85
- stanza = "listen #{watcher.name} localhost:#{watcher.local_port}\n"
609
+ return config
610
+ end
86
611
 
87
- watcher.listen.each do |line|
88
- stanza << "\t#{line}\n"
612
+ # generates an individual stanza for a particular watcher
613
+ def generate_frontend_stanza(watcher, config)
614
+ unless watcher.haproxy.has_key?("port")
615
+ log.debug "synapse: not generating frontend stanza for watcher #{watcher.name} because it has no port defined"
616
+ return []
89
617
  end
90
618
 
91
- watcher.backends.shuffle.each do |backend|
92
- stanza << "\tserver #{backend['name']} #{backend['host']}:#{backend['port']} #{watcher.server_options}\n"
619
+ stanza = [
620
+ "\nfrontend #{watcher.name}",
621
+ config.map {|c| "\t#{c}"},
622
+ "\tbind #{@opts['bind_address'] || 'localhost'}:#{watcher.haproxy['port']}",
623
+ "\tdefault_backend #{watcher.name}"
624
+ ]
625
+ end
626
+
627
+ def generate_backend_stanza(watcher, config)
628
+ if watcher.backends.empty?
629
+ log.warn "synapse: no backends found for watcher #{watcher.name}"
93
630
  end
94
631
 
95
- return stanza
632
+ stanza = [
633
+ "\nbackend #{watcher.name}",
634
+ config.map {|c| "\t#{c}"},
635
+ watcher.backends.shuffle.map {|backend|
636
+ backend_name = construct_name(backend)
637
+ "\tserver #{backend_name} #{backend['host']}:#{backend['port']} #{watcher.haproxy['server_options']}" }
638
+ ]
96
639
  end
97
640
 
98
641
  # tries to set active backends via haproxy's stats socket
@@ -134,13 +677,14 @@ module Synapse
134
677
  end
135
678
 
136
679
  watcher.backends.each do |backend|
137
- unless cur_backends[watcher.name].include? backend['name']
138
- log.debug "synapse: restart required because we have a new backend #{watcher.name}/#{backend['name']}"
680
+ backend_name = construct_name(backend)
681
+ unless cur_backends[watcher.name].include? backend_name
682
+ log.debug "synapse: restart required because we have a new backend #{watcher.name}/#{backend_name}"
139
683
  @restart_required = true
140
684
  return
141
685
  end
142
686
 
143
- enabled_backends[watcher.name] << backend['name']
687
+ enabled_backends[watcher.name] << backend_name
144
688
  end
145
689
  end
146
690
 
@@ -192,9 +736,22 @@ module Synapse
192
736
 
193
737
  # restarts haproxy
194
738
  def restart
739
+ # sleep if we restarted too recently
740
+ delay = (@last_restart - Time.now) + @restart_interval
741
+ sleep(delay) if delay > 0
742
+
743
+ # do the actual restart
195
744
  res = `#{opts['reload_command']}`.chomp
196
745
  raise "failed to reload haproxy via #{opts['reload_command']}: #{res}" unless $?.success?
746
+
747
+ @last_restart = Time.now()
197
748
  @restart_required = false
198
749
  end
750
+
751
+ # used to build unique, consistent haproxy names for backends
752
+ def construct_name(backend)
753
+ address_digest = Digest::SHA256.hexdigest(backend['host'])[0..7]
754
+ return "#{backend['name']}:#{backend['port']}_#{address_digest}"
755
+ end
199
756
  end
200
757
  end