synapse 0.2.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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