synapse-aurora 0.11.2
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.
- data/.gitignore +23 -0
- data/.mailmap +3 -0
- data/.nix/Gemfile.nix +141 -0
- data/.nix/rubylibs.nix +42 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Makefile +6 -0
- data/README.md +339 -0
- data/Rakefile +8 -0
- data/bin/synapse +62 -0
- data/config/hostheader_test.json +71 -0
- data/config/svcdir_test.json +46 -0
- data/config/synapse.conf.json +90 -0
- data/config/synapse_services/service1.json +24 -0
- data/config/synapse_services/service2.json +24 -0
- data/default.nix +66 -0
- data/lib/synapse.rb +85 -0
- data/lib/synapse/base.rb +5 -0
- data/lib/synapse/haproxy.rb +797 -0
- data/lib/synapse/log.rb +24 -0
- data/lib/synapse/service_watcher.rb +36 -0
- data/lib/synapse/service_watcher/base.rb +109 -0
- data/lib/synapse/service_watcher/dns.rb +109 -0
- data/lib/synapse/service_watcher/docker.rb +120 -0
- data/lib/synapse/service_watcher/ec2tag.rb +133 -0
- data/lib/synapse/service_watcher/zookeeper.rb +153 -0
- data/lib/synapse/service_watcher/zookeeper_aurora.rb +76 -0
- data/lib/synapse/service_watcher/zookeeper_dns.rb +232 -0
- data/lib/synapse/version.rb +3 -0
- data/spec/lib/synapse/haproxy_spec.rb +32 -0
- data/spec/lib/synapse/service_watcher_base_spec.rb +55 -0
- data/spec/lib/synapse/service_watcher_docker_spec.rb +152 -0
- data/spec/lib/synapse/service_watcher_ec2tags_spec.rb +220 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/configuration.rb +9 -0
- data/spec/support/minimum.conf.yaml +27 -0
- data/synapse.gemspec +33 -0
- metadata +227 -0
data/lib/synapse/base.rb
ADDED
@@ -0,0 +1,797 @@
|
|
1
|
+
require 'synapse/log'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
module Synapse
|
5
|
+
class Haproxy
|
6
|
+
include Logging
|
7
|
+
attr_reader :opts
|
8
|
+
|
9
|
+
# these come from the documentation for haproxy 1.5
|
10
|
+
# http://haproxy.1wt.eu/download/1.5/doc/configuration.txt
|
11
|
+
@@section_fields = {
|
12
|
+
"backend" => [
|
13
|
+
"acl",
|
14
|
+
"appsession",
|
15
|
+
"balance",
|
16
|
+
"bind-process",
|
17
|
+
"block",
|
18
|
+
"compression",
|
19
|
+
"contimeout",
|
20
|
+
"cookie",
|
21
|
+
"default-server",
|
22
|
+
"description",
|
23
|
+
"disabled",
|
24
|
+
"dispatch",
|
25
|
+
"enabled",
|
26
|
+
"errorfile",
|
27
|
+
"errorloc",
|
28
|
+
"errorloc302",
|
29
|
+
"errorloc303",
|
30
|
+
"force-persist",
|
31
|
+
"fullconn",
|
32
|
+
"grace",
|
33
|
+
"hash-type",
|
34
|
+
"http-check disable-on-404",
|
35
|
+
"http-check expect",
|
36
|
+
"http-check send-state",
|
37
|
+
"http-request",
|
38
|
+
"http-response",
|
39
|
+
"id",
|
40
|
+
"ignore-persist",
|
41
|
+
"log",
|
42
|
+
"mode",
|
43
|
+
"option abortonclose",
|
44
|
+
"option accept-invalid-http-response",
|
45
|
+
"option allbackups",
|
46
|
+
"option checkcache",
|
47
|
+
"option forceclose",
|
48
|
+
"option forwardfor",
|
49
|
+
"option http-no-delay",
|
50
|
+
"option http-pretend-keepalive",
|
51
|
+
"option http-server-close",
|
52
|
+
"option httpchk",
|
53
|
+
"option httpclose",
|
54
|
+
"option httplog",
|
55
|
+
"option http_proxy",
|
56
|
+
"option independent-streams",
|
57
|
+
"option lb-agent-chk",
|
58
|
+
"option ldap-check",
|
59
|
+
"option log-health-checks",
|
60
|
+
"option mysql-check",
|
61
|
+
"option pgsql-check",
|
62
|
+
"option nolinger",
|
63
|
+
"option originalto",
|
64
|
+
"option persist",
|
65
|
+
"option redispatch",
|
66
|
+
"option redis-check",
|
67
|
+
"option smtpchk",
|
68
|
+
"option splice-auto",
|
69
|
+
"option splice-request",
|
70
|
+
"option splice-response",
|
71
|
+
"option srvtcpka",
|
72
|
+
"option ssl-hello-chk",
|
73
|
+
"option tcp-check",
|
74
|
+
"option tcp-smart-connect",
|
75
|
+
"option tcpka",
|
76
|
+
"option tcplog",
|
77
|
+
"option transparent",
|
78
|
+
"persist rdp-cookie",
|
79
|
+
"redirect",
|
80
|
+
"redisp",
|
81
|
+
"redispatch",
|
82
|
+
"reqadd",
|
83
|
+
"reqallow",
|
84
|
+
"reqdel",
|
85
|
+
"reqdeny",
|
86
|
+
"reqiallow",
|
87
|
+
"reqidel",
|
88
|
+
"reqideny",
|
89
|
+
"reqipass",
|
90
|
+
"reqirep",
|
91
|
+
"reqisetbe",
|
92
|
+
"reqitarpit",
|
93
|
+
"reqpass",
|
94
|
+
"reqrep",
|
95
|
+
"reqsetbe",
|
96
|
+
"reqtarpit",
|
97
|
+
"retries",
|
98
|
+
"rspadd",
|
99
|
+
"rspdel",
|
100
|
+
"rspdeny",
|
101
|
+
"rspidel",
|
102
|
+
"rspideny",
|
103
|
+
"rspirep",
|
104
|
+
"rsprep",
|
105
|
+
"server",
|
106
|
+
"source",
|
107
|
+
"srvtimeout",
|
108
|
+
"stats admin",
|
109
|
+
"stats auth",
|
110
|
+
"stats enable",
|
111
|
+
"stats hide-version",
|
112
|
+
"stats http-request",
|
113
|
+
"stats realm",
|
114
|
+
"stats refresh",
|
115
|
+
"stats scope",
|
116
|
+
"stats show-desc",
|
117
|
+
"stats show-legends",
|
118
|
+
"stats show-node",
|
119
|
+
"stats uri",
|
120
|
+
"stick match",
|
121
|
+
"stick on",
|
122
|
+
"stick store-request",
|
123
|
+
"stick store-response",
|
124
|
+
"stick-table",
|
125
|
+
"tcp-check connect",
|
126
|
+
"tcp-check expect",
|
127
|
+
"tcp-check send",
|
128
|
+
"tcp-check send-binary",
|
129
|
+
"tcp-request content",
|
130
|
+
"tcp-request inspect-delay",
|
131
|
+
"tcp-response content",
|
132
|
+
"tcp-response inspect-delay",
|
133
|
+
"timeout check",
|
134
|
+
"timeout connect",
|
135
|
+
"timeout contimeout",
|
136
|
+
"timeout http-keep-alive",
|
137
|
+
"timeout http-request",
|
138
|
+
"timeout queue",
|
139
|
+
"timeout server",
|
140
|
+
"timeout srvtimeout",
|
141
|
+
"timeout tarpit",
|
142
|
+
"timeout tunnel",
|
143
|
+
"transparent",
|
144
|
+
"use-server"
|
145
|
+
],
|
146
|
+
"defaults" => [
|
147
|
+
"backlog",
|
148
|
+
"balance",
|
149
|
+
"bind-process",
|
150
|
+
"clitimeout",
|
151
|
+
"compression",
|
152
|
+
"contimeout",
|
153
|
+
"cookie",
|
154
|
+
"default-server",
|
155
|
+
"default_backend",
|
156
|
+
"disabled",
|
157
|
+
"enabled",
|
158
|
+
"errorfile",
|
159
|
+
"errorloc",
|
160
|
+
"errorloc302",
|
161
|
+
"errorloc303",
|
162
|
+
"fullconn",
|
163
|
+
"grace",
|
164
|
+
"hash-type",
|
165
|
+
"http-check disable-on-404",
|
166
|
+
"http-check send-state",
|
167
|
+
"log",
|
168
|
+
"maxconn",
|
169
|
+
"mode",
|
170
|
+
"monitor-net",
|
171
|
+
"monitor-uri",
|
172
|
+
"option abortonclose",
|
173
|
+
"option accept-invalid-http-request",
|
174
|
+
"option accept-invalid-http-response",
|
175
|
+
"option allbackups",
|
176
|
+
"option checkcache",
|
177
|
+
"option clitcpka",
|
178
|
+
"option contstats",
|
179
|
+
"option dontlog-normal",
|
180
|
+
"option dontlognull",
|
181
|
+
"option forceclose",
|
182
|
+
"option forwardfor",
|
183
|
+
"option http-no-delay",
|
184
|
+
"option http-pretend-keepalive",
|
185
|
+
"option http-server-close",
|
186
|
+
"option http-use-proxy-header",
|
187
|
+
"option httpchk",
|
188
|
+
"option httpclose",
|
189
|
+
"option httplog",
|
190
|
+
"option http_proxy",
|
191
|
+
"option independent-streams",
|
192
|
+
"option lb-agent-chk",
|
193
|
+
"option ldap-check",
|
194
|
+
"option log-health-checks",
|
195
|
+
"option log-separate-errors",
|
196
|
+
"option logasap",
|
197
|
+
"option mysql-check",
|
198
|
+
"option pgsql-check",
|
199
|
+
"option nolinger",
|
200
|
+
"option originalto",
|
201
|
+
"option persist",
|
202
|
+
"option redispatch",
|
203
|
+
"option redis-check",
|
204
|
+
"option smtpchk",
|
205
|
+
"option socket-stats",
|
206
|
+
"option splice-auto",
|
207
|
+
"option splice-request",
|
208
|
+
"option splice-response",
|
209
|
+
"option srvtcpka",
|
210
|
+
"option ssl-hello-chk",
|
211
|
+
"option tcp-check",
|
212
|
+
"option tcp-smart-accept",
|
213
|
+
"option tcp-smart-connect",
|
214
|
+
"option tcpka",
|
215
|
+
"option tcplog",
|
216
|
+
"option transparent",
|
217
|
+
"persist rdp-cookie",
|
218
|
+
"rate-limit sessions",
|
219
|
+
"redisp",
|
220
|
+
"redispatch",
|
221
|
+
"retries",
|
222
|
+
"source",
|
223
|
+
"srvtimeout",
|
224
|
+
"stats auth",
|
225
|
+
"stats enable",
|
226
|
+
"stats hide-version",
|
227
|
+
"stats realm",
|
228
|
+
"stats refresh",
|
229
|
+
"stats scope",
|
230
|
+
"stats show-desc",
|
231
|
+
"stats show-legends",
|
232
|
+
"stats show-node",
|
233
|
+
"stats uri",
|
234
|
+
"timeout check",
|
235
|
+
"timeout client",
|
236
|
+
"timeout clitimeout",
|
237
|
+
"timeout connect",
|
238
|
+
"timeout contimeout",
|
239
|
+
"timeout http-keep-alive",
|
240
|
+
"timeout http-request",
|
241
|
+
"timeout queue",
|
242
|
+
"timeout server",
|
243
|
+
"timeout srvtimeout",
|
244
|
+
"timeout tarpit",
|
245
|
+
"timeout tunnel",
|
246
|
+
"transparent",
|
247
|
+
"unique-id-format",
|
248
|
+
"unique-id-header"
|
249
|
+
],
|
250
|
+
"frontend" => [
|
251
|
+
"acl",
|
252
|
+
"backlog",
|
253
|
+
"bind",
|
254
|
+
"bind-process",
|
255
|
+
"block",
|
256
|
+
"capture cookie",
|
257
|
+
"capture request header",
|
258
|
+
"capture response header",
|
259
|
+
"clitimeout",
|
260
|
+
"compression",
|
261
|
+
"default_backend",
|
262
|
+
"description",
|
263
|
+
"disabled",
|
264
|
+
"enabled",
|
265
|
+
"errorfile",
|
266
|
+
"errorloc",
|
267
|
+
"errorloc302",
|
268
|
+
"errorloc303",
|
269
|
+
"force-persist",
|
270
|
+
"grace",
|
271
|
+
"http-request",
|
272
|
+
"http-response",
|
273
|
+
"id",
|
274
|
+
"ignore-persist",
|
275
|
+
"log",
|
276
|
+
"maxconn",
|
277
|
+
"mode",
|
278
|
+
"monitor fail",
|
279
|
+
"monitor-net",
|
280
|
+
"monitor-uri",
|
281
|
+
"option accept-invalid-http-request",
|
282
|
+
"option clitcpka",
|
283
|
+
"option contstats",
|
284
|
+
"option dontlog-normal",
|
285
|
+
"option dontlognull",
|
286
|
+
"option forceclose",
|
287
|
+
"option forwardfor",
|
288
|
+
"option http-no-delay",
|
289
|
+
"option http-pretend-keepalive",
|
290
|
+
"option http-server-close",
|
291
|
+
"option http-use-proxy-header",
|
292
|
+
"option httpclose",
|
293
|
+
"option httplog",
|
294
|
+
"option http_proxy",
|
295
|
+
"option independent-streams",
|
296
|
+
"option log-separate-errors",
|
297
|
+
"option logasap",
|
298
|
+
"option nolinger",
|
299
|
+
"option originalto",
|
300
|
+
"option socket-stats",
|
301
|
+
"option splice-auto",
|
302
|
+
"option splice-request",
|
303
|
+
"option splice-response",
|
304
|
+
"option tcp-smart-accept",
|
305
|
+
"option tcpka",
|
306
|
+
"option tcplog",
|
307
|
+
"rate-limit sessions",
|
308
|
+
"redirect",
|
309
|
+
"reqadd",
|
310
|
+
"reqallow",
|
311
|
+
"reqdel",
|
312
|
+
"reqdeny",
|
313
|
+
"reqiallow",
|
314
|
+
"reqidel",
|
315
|
+
"reqideny",
|
316
|
+
"reqipass",
|
317
|
+
"reqirep",
|
318
|
+
"reqisetbe",
|
319
|
+
"reqitarpit",
|
320
|
+
"reqpass",
|
321
|
+
"reqrep",
|
322
|
+
"reqsetbe",
|
323
|
+
"reqtarpit",
|
324
|
+
"rspadd",
|
325
|
+
"rspdel",
|
326
|
+
"rspdeny",
|
327
|
+
"rspidel",
|
328
|
+
"rspideny",
|
329
|
+
"rspirep",
|
330
|
+
"rsprep",
|
331
|
+
"tcp-request connection",
|
332
|
+
"tcp-request content",
|
333
|
+
"tcp-request inspect-delay",
|
334
|
+
"timeout client",
|
335
|
+
"timeout clitimeout",
|
336
|
+
"timeout http-keep-alive",
|
337
|
+
"timeout http-request",
|
338
|
+
"timeout tarpit",
|
339
|
+
"unique-id-format",
|
340
|
+
"unique-id-header",
|
341
|
+
"use_backend"
|
342
|
+
],
|
343
|
+
"listen" => [
|
344
|
+
"acl",
|
345
|
+
"appsession",
|
346
|
+
"backlog",
|
347
|
+
"balance",
|
348
|
+
"bind",
|
349
|
+
"bind-process",
|
350
|
+
"block",
|
351
|
+
"capture cookie",
|
352
|
+
"capture request header",
|
353
|
+
"capture response header",
|
354
|
+
"clitimeout",
|
355
|
+
"compression",
|
356
|
+
"contimeout",
|
357
|
+
"cookie",
|
358
|
+
"default-server",
|
359
|
+
"default_backend",
|
360
|
+
"description",
|
361
|
+
"disabled",
|
362
|
+
"dispatch",
|
363
|
+
"enabled",
|
364
|
+
"errorfile",
|
365
|
+
"errorloc",
|
366
|
+
"errorloc302",
|
367
|
+
"errorloc303",
|
368
|
+
"force-persist",
|
369
|
+
"fullconn",
|
370
|
+
"grace",
|
371
|
+
"hash-type",
|
372
|
+
"http-check disable-on-404",
|
373
|
+
"http-check expect",
|
374
|
+
"http-check send-state",
|
375
|
+
"http-request",
|
376
|
+
"http-response",
|
377
|
+
"id",
|
378
|
+
"ignore-persist",
|
379
|
+
"log",
|
380
|
+
"maxconn",
|
381
|
+
"mode",
|
382
|
+
"monitor fail",
|
383
|
+
"monitor-net",
|
384
|
+
"monitor-uri",
|
385
|
+
"option abortonclose",
|
386
|
+
"option accept-invalid-http-request",
|
387
|
+
"option accept-invalid-http-response",
|
388
|
+
"option allbackups",
|
389
|
+
"option checkcache",
|
390
|
+
"option clitcpka",
|
391
|
+
"option contstats",
|
392
|
+
"option dontlog-normal",
|
393
|
+
"option dontlognull",
|
394
|
+
"option forceclose",
|
395
|
+
"option forwardfor",
|
396
|
+
"option http-no-delay",
|
397
|
+
"option http-pretend-keepalive",
|
398
|
+
"option http-server-close",
|
399
|
+
"option http-use-proxy-header",
|
400
|
+
"option httpchk",
|
401
|
+
"option httpclose",
|
402
|
+
"option httplog",
|
403
|
+
"option http_proxy",
|
404
|
+
"option independent-streams",
|
405
|
+
"option lb-agent-chk",
|
406
|
+
"option ldap-check",
|
407
|
+
"option log-health-checks",
|
408
|
+
"option log-separate-errors",
|
409
|
+
"option logasap",
|
410
|
+
"option mysql-check",
|
411
|
+
"option pgsql-check",
|
412
|
+
"option nolinger",
|
413
|
+
"option originalto",
|
414
|
+
"option persist",
|
415
|
+
"option redispatch",
|
416
|
+
"option redis-check",
|
417
|
+
"option smtpchk",
|
418
|
+
"option socket-stats",
|
419
|
+
"option splice-auto",
|
420
|
+
"option splice-request",
|
421
|
+
"option splice-response",
|
422
|
+
"option srvtcpka",
|
423
|
+
"option ssl-hello-chk",
|
424
|
+
"option tcp-check",
|
425
|
+
"option tcp-smart-accept",
|
426
|
+
"option tcp-smart-connect",
|
427
|
+
"option tcpka",
|
428
|
+
"option tcplog",
|
429
|
+
"option transparent",
|
430
|
+
"persist rdp-cookie",
|
431
|
+
"rate-limit sessions",
|
432
|
+
"redirect",
|
433
|
+
"redisp",
|
434
|
+
"redispatch",
|
435
|
+
"reqadd",
|
436
|
+
"reqallow",
|
437
|
+
"reqdel",
|
438
|
+
"reqdeny",
|
439
|
+
"reqiallow",
|
440
|
+
"reqidel",
|
441
|
+
"reqideny",
|
442
|
+
"reqipass",
|
443
|
+
"reqirep",
|
444
|
+
"reqisetbe",
|
445
|
+
"reqitarpit",
|
446
|
+
"reqpass",
|
447
|
+
"reqrep",
|
448
|
+
"reqsetbe",
|
449
|
+
"reqtarpit",
|
450
|
+
"retries",
|
451
|
+
"rspadd",
|
452
|
+
"rspdel",
|
453
|
+
"rspdeny",
|
454
|
+
"rspidel",
|
455
|
+
"rspideny",
|
456
|
+
"rspirep",
|
457
|
+
"rsprep",
|
458
|
+
"server",
|
459
|
+
"source",
|
460
|
+
"srvtimeout",
|
461
|
+
"stats admin",
|
462
|
+
"stats auth",
|
463
|
+
"stats enable",
|
464
|
+
"stats hide-version",
|
465
|
+
"stats http-request",
|
466
|
+
"stats realm",
|
467
|
+
"stats refresh",
|
468
|
+
"stats scope",
|
469
|
+
"stats show-desc",
|
470
|
+
"stats show-legends",
|
471
|
+
"stats show-node",
|
472
|
+
"stats uri",
|
473
|
+
"stick match",
|
474
|
+
"stick on",
|
475
|
+
"stick store-request",
|
476
|
+
"stick store-response",
|
477
|
+
"stick-table",
|
478
|
+
"tcp-check connect",
|
479
|
+
"tcp-check expect",
|
480
|
+
"tcp-check send",
|
481
|
+
"tcp-check send-binary",
|
482
|
+
"tcp-request connection",
|
483
|
+
"tcp-request content",
|
484
|
+
"tcp-request inspect-delay",
|
485
|
+
"tcp-response content",
|
486
|
+
"tcp-response inspect-delay",
|
487
|
+
"timeout check",
|
488
|
+
"timeout client",
|
489
|
+
"timeout clitimeout",
|
490
|
+
"timeout connect",
|
491
|
+
"timeout contimeout",
|
492
|
+
"timeout http-keep-alive",
|
493
|
+
"timeout http-request",
|
494
|
+
"timeout queue",
|
495
|
+
"timeout server",
|
496
|
+
"timeout srvtimeout",
|
497
|
+
"timeout tarpit",
|
498
|
+
"timeout tunnel",
|
499
|
+
"transparent",
|
500
|
+
"unique-id-format",
|
501
|
+
"unique-id-header",
|
502
|
+
"use_backend",
|
503
|
+
"use-server"
|
504
|
+
]
|
505
|
+
}
|
506
|
+
|
507
|
+
def initialize(opts)
|
508
|
+
super()
|
509
|
+
|
510
|
+
%w{global defaults reload_command}.each do |req|
|
511
|
+
raise ArgumentError, "haproxy requires a #{req} section" if !opts.has_key?(req)
|
512
|
+
end
|
513
|
+
|
514
|
+
req_pairs = {
|
515
|
+
'do_writes' => 'config_file_path',
|
516
|
+
'do_socket' => 'socket_file_path',
|
517
|
+
'do_reloads' => 'reload_command'}
|
518
|
+
|
519
|
+
req_pairs.each do |cond, req|
|
520
|
+
if opts[cond]
|
521
|
+
raise ArgumentError, "the `#{req}` option is required when `#{cond}` is true" unless opts[req]
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
@opts = opts
|
526
|
+
|
527
|
+
# how to restart haproxy
|
528
|
+
@restart_interval = 2
|
529
|
+
@restart_required = true
|
530
|
+
@last_restart = Time.new(0)
|
531
|
+
|
532
|
+
# a place to store the parsed haproxy config from each watcher
|
533
|
+
@watcher_configs = {}
|
534
|
+
end
|
535
|
+
|
536
|
+
def update_config(watchers)
|
537
|
+
# if we support updating backends, try that whenever possible
|
538
|
+
if @opts['do_socket']
|
539
|
+
update_backends(watchers) unless @restart_required
|
540
|
+
else
|
541
|
+
@restart_required = true
|
542
|
+
end
|
543
|
+
|
544
|
+
# generate a new config
|
545
|
+
new_config = generate_config(watchers)
|
546
|
+
|
547
|
+
# if we write config files, lets do that and then possibly restart
|
548
|
+
if @opts['do_writes']
|
549
|
+
write_config(new_config)
|
550
|
+
restart if @opts['do_reloads'] && @restart_required
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
# generates a new config based on the state of the watchers
|
555
|
+
def generate_config(watchers)
|
556
|
+
new_config = generate_base_config
|
557
|
+
shared_frontend_lines = generate_shared_frontend
|
558
|
+
|
559
|
+
watchers.each do |watcher|
|
560
|
+
@watcher_configs[watcher.name] ||= parse_watcher_config(watcher)
|
561
|
+
new_config << generate_frontend_stanza(watcher, @watcher_configs[watcher.name]['frontend'])
|
562
|
+
new_config << generate_backend_stanza(watcher, @watcher_configs[watcher.name]['backend'])
|
563
|
+
if watcher.haproxy.include?('shared_frontend')
|
564
|
+
if @opts['shared_frontend'] == nil
|
565
|
+
log.warn "synapse: service #{watcher.name} contains a shared frontend section but the base config does not! skipping."
|
566
|
+
else
|
567
|
+
shared_frontend_lines << validate_haproxy_stanza(watcher.haproxy['shared_frontend'].map{|l| "\t#{l}"}, "frontend", "shared frontend section for #{watcher.name}")
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
new_config << shared_frontend_lines.flatten if shared_frontend_lines
|
572
|
+
|
573
|
+
log.debug "synapse: new haproxy config: #{new_config}"
|
574
|
+
return new_config.flatten.join("\n")
|
575
|
+
end
|
576
|
+
|
577
|
+
# pull out the shared frontend section if any
|
578
|
+
def generate_shared_frontend
|
579
|
+
return nil unless @opts.include?('shared_frontend')
|
580
|
+
log.debug "synapse: found a shared frontend section"
|
581
|
+
shared_frontend_lines = ["\nfrontend shared-frontend"]
|
582
|
+
shared_frontend_lines << validate_haproxy_stanza(@opts['shared_frontend'].map{|l| "\t#{l}"}, "frontend", "shared frontend")
|
583
|
+
return shared_frontend_lines
|
584
|
+
end
|
585
|
+
|
586
|
+
# generates the global and defaults sections of the config file
|
587
|
+
def generate_base_config
|
588
|
+
base_config = ["# auto-generated by synapse at #{Time.now}\n"]
|
589
|
+
|
590
|
+
%w{global defaults}.each do |section|
|
591
|
+
base_config << "#{section}"
|
592
|
+
@opts[section].each do |option|
|
593
|
+
base_config << "\t#{option}"
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
if @opts['extra_sections']
|
598
|
+
@opts['extra_sections'].each do |title, section|
|
599
|
+
base_config << "\n#{title}"
|
600
|
+
section.each do |option|
|
601
|
+
base_config << "\t#{option}"
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
return base_config
|
607
|
+
end
|
608
|
+
|
609
|
+
# split the haproxy config in each watcher into fields applicable in
|
610
|
+
# frontend and backend sections
|
611
|
+
def parse_watcher_config(watcher)
|
612
|
+
config = {}
|
613
|
+
%w{frontend backend}.each do |section|
|
614
|
+
config[section] = watcher.haproxy[section] || []
|
615
|
+
|
616
|
+
# copy over the settings from the 'listen' section that pertain to section
|
617
|
+
config[section].concat(
|
618
|
+
watcher.haproxy['listen'].select {|setting|
|
619
|
+
parsed_setting = setting.strip.gsub(/\s+/, ' ').downcase
|
620
|
+
@@section_fields[section].any? {|field| parsed_setting.start_with?(field)}
|
621
|
+
})
|
622
|
+
|
623
|
+
# pick only those fields that are valid and warn about the invalid ones
|
624
|
+
config[section] = validate_haproxy_stanza(config[section], section, watcher.name)
|
625
|
+
end
|
626
|
+
|
627
|
+
return config
|
628
|
+
end
|
629
|
+
|
630
|
+
def validate_haproxy_stanza(stanza, stanza_type, service_name)
|
631
|
+
return stanza.select {|setting|
|
632
|
+
parsed_setting = setting.strip.gsub(/\s+/, ' ').downcase
|
633
|
+
if @@section_fields[stanza_type].any? {|field| parsed_setting.start_with?(field)}
|
634
|
+
true
|
635
|
+
else
|
636
|
+
log.warn "synapse: service #{service_name} contains invalid #{stanza_type} setting: '#{setting}', discarding"
|
637
|
+
false
|
638
|
+
end
|
639
|
+
}
|
640
|
+
end
|
641
|
+
|
642
|
+
# generates an individual stanza for a particular watcher
|
643
|
+
def generate_frontend_stanza(watcher, config)
|
644
|
+
unless watcher.haproxy.has_key?("port")
|
645
|
+
log.debug "synapse: not generating frontend stanza for watcher #{watcher.name} because it has no port defined"
|
646
|
+
return []
|
647
|
+
end
|
648
|
+
|
649
|
+
stanza = [
|
650
|
+
"\nfrontend #{watcher.name}",
|
651
|
+
config.map {|c| "\t#{c}"},
|
652
|
+
"\tbind #{@opts['bind_address'] || 'localhost'}:#{watcher.haproxy['port']}",
|
653
|
+
"\tdefault_backend #{watcher.name}"
|
654
|
+
]
|
655
|
+
end
|
656
|
+
|
657
|
+
def generate_backend_stanza(watcher, config)
|
658
|
+
if watcher.backends.empty?
|
659
|
+
log.warn "synapse: no backends found for watcher #{watcher.name}"
|
660
|
+
end
|
661
|
+
|
662
|
+
stanza = [
|
663
|
+
"\nbackend #{watcher.name}",
|
664
|
+
config.map {|c| "\t#{c}"},
|
665
|
+
watcher.backends.shuffle.map {|backend|
|
666
|
+
backend_name = construct_name(backend)
|
667
|
+
b = "\tserver #{backend_name} #{backend['host']}:#{backend['port']}"
|
668
|
+
b = "#{b} cookie #{backend_name}" unless config.include?('mode tcp')
|
669
|
+
b = "#{b} #{watcher.haproxy['server_options']}"
|
670
|
+
b }
|
671
|
+
]
|
672
|
+
end
|
673
|
+
|
674
|
+
# tries to set active backends via haproxy's stats socket
|
675
|
+
# because we can't add backends via the socket, we might still need to restart haproxy
|
676
|
+
def update_backends(watchers)
|
677
|
+
# first, get a list of existing servers for various backends
|
678
|
+
begin
|
679
|
+
s = UNIXSocket.new(@opts['socket_file_path'])
|
680
|
+
s.write("show stat\n")
|
681
|
+
info = s.read()
|
682
|
+
rescue StandardError => e
|
683
|
+
log.warn "synapse: unhandled error reading stats socket: #{e.inspect}"
|
684
|
+
@restart_required = true
|
685
|
+
return
|
686
|
+
end
|
687
|
+
|
688
|
+
# parse the stats output to get current backends
|
689
|
+
cur_backends = {}
|
690
|
+
info.split("\n").each do |line|
|
691
|
+
next if line[0] == '#'
|
692
|
+
|
693
|
+
parts = line.split(',')
|
694
|
+
next if ['FRONTEND', 'BACKEND'].include?(parts[1])
|
695
|
+
|
696
|
+
cur_backends[parts[0]] ||= []
|
697
|
+
cur_backends[parts[0]] << parts[1]
|
698
|
+
end
|
699
|
+
|
700
|
+
# build a list of backends that should be enabled
|
701
|
+
enabled_backends = {}
|
702
|
+
watchers.each do |watcher|
|
703
|
+
enabled_backends[watcher.name] = []
|
704
|
+
next if watcher.backends.empty?
|
705
|
+
|
706
|
+
unless cur_backends.include? watcher.name
|
707
|
+
log.debug "synapse: restart required because we added new section #{watcher.name}"
|
708
|
+
@restart_required = true
|
709
|
+
return
|
710
|
+
end
|
711
|
+
|
712
|
+
watcher.backends.each do |backend|
|
713
|
+
backend_name = construct_name(backend)
|
714
|
+
unless cur_backends[watcher.name].include? backend_name
|
715
|
+
log.debug "synapse: restart required because we have a new backend #{watcher.name}/#{backend_name}"
|
716
|
+
@restart_required = true
|
717
|
+
return
|
718
|
+
end
|
719
|
+
|
720
|
+
enabled_backends[watcher.name] << backend_name
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
# actually enable the enabled backends, and disable the disabled ones
|
725
|
+
cur_backends.each do |section, backends|
|
726
|
+
backends.each do |backend|
|
727
|
+
if enabled_backends[section].include? backend
|
728
|
+
command = "enable server #{section}/#{backend}\n"
|
729
|
+
else
|
730
|
+
command = "disable server #{section}/#{backend}\n"
|
731
|
+
end
|
732
|
+
|
733
|
+
# actually write the command to the socket
|
734
|
+
begin
|
735
|
+
s = UNIXSocket.new(@opts['socket_file_path'])
|
736
|
+
s.write(command)
|
737
|
+
output = s.read()
|
738
|
+
rescue StandardError => e
|
739
|
+
log.warn "synapse: unknown error writing to socket"
|
740
|
+
@restart_required = true
|
741
|
+
return
|
742
|
+
else
|
743
|
+
unless output == "\n"
|
744
|
+
log.warn "synapse: socket command #{command} failed: #{output}"
|
745
|
+
@restart_required = true
|
746
|
+
return
|
747
|
+
end
|
748
|
+
end
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
log.info "synapse: reconfigured haproxy"
|
753
|
+
end
|
754
|
+
|
755
|
+
# writes the config
|
756
|
+
def write_config(new_config)
|
757
|
+
begin
|
758
|
+
old_config = File.read(@opts['config_file_path'])
|
759
|
+
rescue Errno::ENOENT => e
|
760
|
+
log.info "synapse: could not open haproxy config file at #{@opts['config_file_path']}"
|
761
|
+
old_config = ""
|
762
|
+
end
|
763
|
+
|
764
|
+
if old_config == new_config
|
765
|
+
return false
|
766
|
+
else
|
767
|
+
File.open(@opts['config_file_path'],'w') {|f| f.write(new_config)}
|
768
|
+
return true
|
769
|
+
end
|
770
|
+
end
|
771
|
+
|
772
|
+
# restarts haproxy
|
773
|
+
def restart
|
774
|
+
# sleep if we restarted too recently
|
775
|
+
delay = (@last_restart - Time.now) + @restart_interval
|
776
|
+
sleep(delay) if delay > 0
|
777
|
+
|
778
|
+
# do the actual restart
|
779
|
+
res = `#{opts['reload_command']}`.chomp
|
780
|
+
raise "failed to reload haproxy via #{opts['reload_command']}: #{res}" unless $?.success?
|
781
|
+
log.info "synapse: restarted haproxy"
|
782
|
+
|
783
|
+
@last_restart = Time.now()
|
784
|
+
@restart_required = false
|
785
|
+
end
|
786
|
+
|
787
|
+
# used to build unique, consistent haproxy names for backends
|
788
|
+
def construct_name(backend)
|
789
|
+
name = "#{backend['host']}:#{backend['port']}"
|
790
|
+
if backend['name'] && !backend['name'].empty?
|
791
|
+
name = "#{name}_#{backend['name']}"
|
792
|
+
end
|
793
|
+
|
794
|
+
return name
|
795
|
+
end
|
796
|
+
end
|
797
|
+
end
|