synapse 0.13.8 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZDAyZjEyZDU5ZGUwMmMwOTFkNjgyMmUwMmU2NGRlYTBmMWQ5MmE3YQ==
4
+ YTEwNDE5ZTdlZDM4Njk2YmJkMDRjYWQzYTNiM2FkZTM0MzgyZWE4YQ==
5
5
  data.tar.gz: !binary |-
6
- YzNjYjQ2NWJkNDAyYzllZmE0ZGJjNWFmZmQ5YTdmMWU2NjlhYjQ4Mg==
6
+ ZjBmNmVmOTY2YWRiYTY1NWZmYTg3YjhiMThhY2NmNjIxNmE3ODkwYQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NGE5ZjEwYmM0MmE2MjgyYjY5YTQzN2Q3YjY0YzliMjFjNmYwNWUwMDAyN2M5
10
- ZTg2MmQ0ZWNlNTM2OTQyZjk4MWMzN2EzMGNiNjM1ZGE4MjE2OGQ3OTRiZjMx
11
- M2U2NmNmZDkyODQxMDI4NjYwNmI3NmYzY2M2ZGM1NzFhMmVkZTQ=
9
+ NjhlNDA1YjY1ODkxZWU4YzMyMzI2ODE1YWNjMzlmZWM4MTk1OTVjZTVkMjc4
10
+ ODFjMTVlNDEyYjVmMDk0YTAxMjM0MWMyNGRmOGFiYjdhNDM4OWFiNDFlMjE1
11
+ ZDMzNzZiYjg2MjgzY2VmYjQ2YjAxOGEwOTE0Y2MwMTkwNGFjMmU=
12
12
  data.tar.gz: !binary |-
13
- MzAxZGIyYTRiNDdlYjYwYzY1NzM0ZDlhMTllNmNkYjdiYzQzODFlOGFmYmJi
14
- NTQ1MmIzOTI3Y2IxZmQyZTYwYzNiMDRiMTM3ZGZmOTc1OTAzMmQyZWEzYTY5
15
- MmE1NDQ3NDUxZGI3MGE3NGVhZDdiODE1NzA2NTk5ZjFlNmI0NGY=
13
+ MGI0ZTBjNGQ5NjA0NmU1OWI0NzJhYmYyNDI2NWEyODI0MjA1NDM5NmYxNzEw
14
+ YWYxMzI3MDc2ZTEyMDFkM2U5MzkyY2U5NzBkNjQ4YjA4YWNhNTI0YmRlMzlj
15
+ N2JhM2MxYjQ5YWVhMzBlZWFhZTM3Y2RkMzVhOTNlMzc0ZmVkY2Y=
data/.travis.yml CHANGED
@@ -2,7 +2,8 @@ language: ruby
2
2
  cache: bundler
3
3
  sudo: false
4
4
  rvm:
5
- - 1.9.3
6
- - 2.0.0
7
- - 2.1.6
8
- - 2.2.2
5
+ - 1.9.3-p551
6
+ - 2.0.0-p648
7
+ - 2.1.10
8
+ - 2.2.5
9
+ - 2.3.1
data/README.md CHANGED
@@ -134,12 +134,24 @@ The file has three main sections.
134
134
 
135
135
  The `services` section is a hash, where the keys are the `name` of the service to be configured.
136
136
  The name is just a human-readable string; it will be used in logs and notifications.
137
- Each value in the services hash is also a hash, and should contain the following keys:
137
+ Each value in the services hash is also a hash, and must contain the following keys:
138
+
139
+ * [`discovery`](#discovery): how synapse will discover hosts providing this service (see [below](#discovery))
140
+
141
+ The services hash *should* contain a section on how to configure the routing
142
+ component you wish to use for this particular service. The only choice currently
143
+ is `haproxy`:
138
144
 
139
- * [`discovery`](#discovery): how synapse will discover hosts providing this service (see below)
140
- * `default_servers`: the list of default servers providing this service; synapse uses these if no others can be discovered
141
145
  * [`haproxy`](#haproxysvc): how will the haproxy section for this service be configured
142
146
 
147
+ The services hash may contain the following keys:
148
+
149
+ * `default_servers` (default: `[]`): the list of default servers providing this service; synapse uses these if no others can be discovered. See [Listing Default Servers](#defaultservers).
150
+ * `keep_default_servers` (default: false): whether default servers should be added to discovered services
151
+ * `use_previous_backends` (default: true): if at any time the registry drops all backends, use previous backends we already know about.
152
+ <a name="backend_port_override"/>
153
+ * `backend_port_override`: the port that discovered servers listen on; you should specify this if your discovery mechanism only discovers names or addresses (like the DNS watcher or the Ec2TagWatcher). If the discovery method discovers a port along with hostnames (like the zookeeper watcher) this option may be left out, but will be used in preference if given.
154
+
143
155
  <a name="discovery"/>
144
156
  #### Service Discovery ####
145
157
 
@@ -214,9 +226,9 @@ It takes the following options:
214
226
  this is case-sensitive.
215
227
  * `tag_value`: the value to match on. Case-sensitive.
216
228
 
217
- Additionally, you MUST supply `server_port_override` in the `haproxy`
218
- section of the configuration as this watcher does not know which port
219
- the backend service is listening on.
229
+ Additionally, you MUST supply [`backend_port_override`](#backend_port_override)
230
+ in the service configuration as this watcher does not know which port the
231
+ backend service is listening on.
220
232
 
221
233
  The following options are optional, provided the well-known `AWS_`
222
234
  environment variables shown are set. If supplied, these options will
@@ -238,6 +250,7 @@ It takes the following options:
238
250
  * `check_interval`: How often to request the list of tasks from Marathon (default: 10 seconds)
239
251
  * `port_index`: Index of the backend port in the task's "ports" array. (default: 0)
240
252
 
253
+ <a name="defaultservers"/>
241
254
  #### Listing Default Servers ####
242
255
 
243
256
  You may list a number of default servers providing a service.
@@ -262,9 +275,13 @@ by unsetting `use_previous_backends`.
262
275
 
263
276
  This section is its own hash, which should contain the following keys:
264
277
 
278
+ * `disabled`: A boolean value indicating if haproxy configuration management
279
+ for just this service instance ought be disabled. For example, if you want
280
+ file output for a particular service but no HAProxy config. (default is ``False``)
265
281
  * `port`: the port (on localhost) where HAProxy will listen for connections to the service. If this is omitted, only a backend stanza (and no frontend stanza) will be generated for this service; you'll need to get traffic to your service yourself via the `shared_frontend` or manual frontends in `extra_sections`
266
282
  * `bind_address`: force HAProxy to listen on this address ( default is localhost ). Setting `bind_address` on a per service basis overrides the global `bind_address` in the top level `haproxy`. Having HAProxy listen for connections on different addresses ( example: service1 listen on 127.0.0.2:443 and service2 listen on 127.0.0.3:443) allows /etc/hosts entries to point to services.
267
- * `server_port_override`: the port that discovered servers listen on; you should specify this if your discovery mechanism only discovers names or addresses (like the DNS watcher). If the discovery method discovers a port along with hostnames (like the zookeeper watcher) this option may be left out, but will be used in preference if given.
283
+ * `bind_options`: optional: default value is an empty string, specify additional bind parameters, such as ssl accept-proxy, crt, ciphers etc.
284
+ * `server_port_override`: **DEPRECATED**. Renamed [`backend_port_override`](#backend_port_override) and moved to the top level hash. This will be removed in future versions.
268
285
  * `server_options`: the haproxy options for each `server` line of the service in HAProxy config; it may be left out.
269
286
  * `frontend`: additional lines passed to the HAProxy config in the `frontend` stanza of this service
270
287
  * `backend`: additional lines passed to the HAProxy config in the `backend` stanza of this service
@@ -371,7 +388,7 @@ For example:
371
388
  server_options: "check inter 2s rise 3 fall 2"
372
389
  shared_frontend:
373
390
  - "acl is_service1 hdr_dom(host) -i service2.lb.example.com"
374
- - "use_backend service2 if is_service2
391
+ - "use_backend service2 if is_service2"
375
392
  backend:
376
393
  - "mode http"
377
394
 
@@ -410,3 +427,8 @@ Non-HTTP backends such as MySQL or RabbitMQ will obviously continue to need thei
410
427
 
411
428
  See the Service Watcher [README](lib/synapse/service_watcher/README.md) for
412
429
  how to add new Service Watchers.
430
+
431
+ ### Creating a Config Generator ###
432
+
433
+ See the Config Generator [README](lib/synapse/config_generator/README.md) for
434
+ how to add new Config Generators
@@ -18,6 +18,7 @@
18
18
  "haproxy": {
19
19
  "port": 3213,
20
20
  "server_options": "check inter 2s rise 3 fall 2",
21
+ "bind_options": "ssl no-sslv3 crt /path/to/cert/example.pem ciphers ECDHE-ECDSA-CHACHA20-POLY1305",
21
22
  "listen": [
22
23
  "mode http",
23
24
  "option httpchk /health",
data/lib/synapse.rb CHANGED
@@ -1,11 +1,10 @@
1
1
  require 'logger'
2
2
  require 'json'
3
3
 
4
- require "synapse/version"
5
- require "synapse/log"
6
- require "synapse/haproxy"
7
- require "synapse/file_output"
8
- require "synapse/service_watcher"
4
+ require 'synapse/version'
5
+ require 'synapse/log'
6
+ require 'synapse/config_generator'
7
+ require 'synapse/service_watcher'
9
8
 
10
9
 
11
10
  module Synapse
@@ -14,22 +13,14 @@ module Synapse
14
13
  include Logging
15
14
 
16
15
  def initialize(opts={})
16
+ # create objects that need to be notified of service changes
17
+ @config_generators = create_config_generators(opts)
18
+ raise "no config generators supplied" if @config_generators.empty?
19
+
17
20
  # create the service watchers for all our services
18
21
  raise "specify a list of services to connect in the config" unless opts.has_key?('services')
19
22
  @service_watchers = create_service_watchers(opts['services'])
20
23
 
21
- # create objects that need to be notified of service changes
22
- @config_generators = []
23
- # create the haproxy config generator, this is mandatory
24
- raise "haproxy config section is missing" unless opts.has_key?('haproxy')
25
- @config_generators << Haproxy.new(opts['haproxy'])
26
-
27
- # possibly create a file manifestation for services that do not
28
- # want to communicate via haproxy, e.g. cassandra
29
- if opts.has_key?('file_output')
30
- @config_generators << FileOutput.new(opts['file_output'])
31
- end
32
-
33
24
  # configuration is initially enabled to configure on first loop
34
25
  @config_updated = true
35
26
 
@@ -85,9 +76,13 @@ module Synapse
85
76
  @config_updated = true
86
77
  end
87
78
 
79
+ def available_generators
80
+ Hash[@config_generators.collect{|cg| [cg.name, cg]}]
81
+ end
82
+
88
83
  private
89
84
  def create_service_watchers(services={})
90
- service_watchers =[]
85
+ service_watchers = []
91
86
  services.each do |service_name, service_config|
92
87
  service_watchers << ServiceWatcher.create(service_name, service_config, self)
93
88
  end
@@ -95,5 +90,16 @@ module Synapse
95
90
  return service_watchers
96
91
  end
97
92
 
93
+ private
94
+ def create_config_generators(opts={})
95
+ config_generators = []
96
+ opts.each do |type, generator_opts|
97
+ # Skip the "services" top level key
98
+ next if type == 'services'
99
+ config_generators << ConfigGenerator.create(type, generator_opts)
100
+ end
101
+
102
+ return config_generators
103
+ end
98
104
  end
99
105
  end
@@ -0,0 +1,20 @@
1
+ require 'synapse/log'
2
+ require 'synapse/config_generator/base'
3
+
4
+ module Synapse
5
+ class ConfigGenerator
6
+ # the type which actually dispatches generator creation requests
7
+ def self.create(type, opts)
8
+ generator = begin
9
+ type = type.downcase
10
+ require "synapse/config_generator/#{type}"
11
+ # haproxy => Haproxy, file_output => FileOutput, etc ...
12
+ type_class = type.split('_').map{|x| x.capitalize}.join
13
+ self.const_get("#{type_class}")
14
+ rescue Exception => e
15
+ raise ArgumentError, "Specified a config generator of #{type}, which could not be found: #{e}"
16
+ end
17
+ return generator.new(opts)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,74 @@
1
+ ## ConfigGenerator Classes
2
+
3
+ Generators are the piece of Synapse that react to changes in service
4
+ registrations and actually reflect those changes in local state.
5
+ Generators should conform to the interface specified by `BaseGenerator` and
6
+ when your generator has received an update from synapse via `update_config` it
7
+ should sync the watcher state with the external configuration (e.g. HAProxy
8
+ state)
9
+
10
+ ```ruby
11
+ require "synapse/config_generator/base"
12
+
13
+ class Synapse::ConfigGenerator
14
+ class MyGenerator < BaseGenerator
15
+ # The generator name is used to find service specific
16
+ # configuration in the service watchers. When supplying
17
+ # per service config, use this as the key
18
+ NAME = 'my_generator'.freeze
19
+
20
+ def initialize(opts = {})
21
+ # Process and validate any options specified in the dedicated section
22
+ # for this config generator, given as the `opts` hash. You may omit
23
+ # this method, or you can declare your own, but remember to invoke
24
+ # the parent initializer
25
+ super(opts)
26
+ end
27
+
28
+ def update_config(watchers)
29
+ # synapse will call this method whenever watcher state changes with the
30
+ # watcher state. You should reflect that state in the local config state
31
+ end
32
+
33
+ def tick
34
+ # Called every loop of the main Synapse loop regardless of watcher
35
+ # changes (roughly ~1s). You can use this to rate limit how often your
36
+ # config generator actually reconfigures external services (e.g. HAProxy
37
+ # may need to rate limit reloads as those can be disruptive to in
38
+ # flight connections
39
+ end
40
+
41
+ def normalize_watcher_provided_config(service_watcher_name, service_watcher_config)
42
+ # Every service watcher section of the Synapse configuration can contain
43
+ # options that change how the config generators react for that
44
+ # particular service. This normalize method is a good place to ensure
45
+ # you set options your generator expects every service watcher config
46
+ # to supply, providing, for example, default values. This is also a
47
+ # good place to raise errors in case any options are invalid.
48
+ end
49
+ end
50
+ end
51
+ ```
52
+
53
+ ### Generator Plugin Inteface
54
+ Synapse deduces both the class path and class name from any additional keys
55
+ passed to the top level configuration, which it assumes are equal to the `NAME`
56
+ of some ConfigGenerator. For example, if `haproxy` is set at the top level we
57
+ try to load the `Haproxy` `ConfigGenerator`.
58
+
59
+ #### Class Location
60
+ Synapse expects to find your class at `synapse/config_generator/#{name}`. You
61
+ must make your generator available at that path, and Synapse can "just work" and
62
+ find it.
63
+
64
+ #### Class Name
65
+ These type strings are then transformed into class names via the following
66
+ function:
67
+
68
+ ```
69
+ type_class = type.split('_').map{|x| x.capitalize}
70
+ ```
71
+
72
+ This has the effect of taking the method, splitting on `_`, capitalizing each
73
+ part and recombining. So `file_output` becomes `FileOutput` and `haproxy`
74
+ becomes `Haproxy`. Make sure your class name is correct.
@@ -0,0 +1,44 @@
1
+ require 'synapse/log'
2
+
3
+ class Synapse::ConfigGenerator
4
+ class BaseGenerator
5
+ include Synapse::Logging
6
+
7
+ NAME = 'base'.freeze
8
+
9
+ attr_reader :opts
10
+
11
+ def initialize(opts={})
12
+ @opts = opts
13
+ end
14
+
15
+ # Exposes NAME as 'name' so we can remain consistent with how we refer to
16
+ # service_watchers' by generator.name access (instead of
17
+ # generator.class::NAME) even though the names of generators don't change
18
+ def name
19
+ self.class::NAME
20
+ end
21
+
22
+ # The synapse main loop will call this any time watchers change, the
23
+ # config_generator is responsible for diffing the passed watcher state
24
+ # against the output configuration
25
+ def update_config(watchers)
26
+ end
27
+
28
+ # The synapse main loop will call this every tick
29
+ # of the logical clock (~1s). You can use this to intiate reloads
30
+ # or restarts in a rate limited fashion
31
+ def tick
32
+ end
33
+
34
+ # Service watchers have a subsection of their ``services`` entry that is
35
+ # dedicated to the watcher specific configuration for how to configure
36
+ # the config generator. This method will be called with each of these
37
+ # watcher hashes, and should normalize them to what the config generator
38
+ # needs, such as adding defaults. Return the properly populated default hash
39
+ def normalize_watcher_provided_config(service_watcher_name, service_watcher_config)
40
+ service_watcher_config.dup
41
+ end
42
+
43
+ end
44
+ end
@@ -1,12 +1,17 @@
1
+ require 'synapse/config_generator/base'
2
+
1
3
  require 'fileutils'
2
4
  require 'tempfile'
3
5
 
4
- module Synapse
5
- class FileOutput
6
- include Logging
7
- attr_reader :opts, :name
6
+ class Synapse::ConfigGenerator
7
+ class FileOutput < BaseGenerator
8
+ include Synapse::Logging
9
+
10
+ NAME = 'file_output'.freeze
8
11
 
9
12
  def initialize(opts)
13
+ super(opts)
14
+
10
15
  unless opts.has_key?("output_directory")
11
16
  raise ArgumentError, "flat file generation requires an output_directory key"
12
17
  end
@@ -16,9 +21,6 @@ module Synapse
16
21
  rescue SystemCallError => err
17
22
  raise ArgumentError, "provided output directory #{opts['output_directory']} is not present or creatable"
18
23
  end
19
-
20
- @opts = opts
21
- @name = 'file_output'
22
24
  end
23
25
 
24
26
  def tick(watchers)
@@ -32,7 +34,7 @@ module Synapse
32
34
  end
33
35
 
34
36
  def write_backends_to_file(service_name, new_backends)
35
- data_path = File.join(@opts['output_directory'], "#{service_name}.json")
37
+ data_path = File.join(opts['output_directory'], "#{service_name}.json")
36
38
  begin
37
39
  old_backends = JSON.load(File.read(data_path))
38
40
  rescue Errno::ENOENT
@@ -45,8 +47,8 @@ module Synapse
45
47
  # internal state only when the smartstack state has actually changed
46
48
  return false
47
49
  else
48
- # Atomically write new sevice configuration file
49
- temp_path = File.join(@opts['output_directory'],
50
+ # Atomically write new service configuration file
51
+ temp_path = File.join(opts['output_directory'],
50
52
  ".#{service_name}.json.tmp")
51
53
  File.open(temp_path, 'w', 0644) {|f| f.write(new_backends.to_json)}
52
54
  FileUtils.mv(temp_path, data_path)
@@ -56,7 +58,7 @@ module Synapse
56
58
 
57
59
  def clean_old_watchers(current_watchers)
58
60
  # Cleanup old services that Synapse no longer manages
59
- FileUtils.cd(@opts['output_directory']) do
61
+ FileUtils.cd(opts['output_directory']) do
60
62
  present_files = Dir.glob('*.json')
61
63
  managed_files = current_watchers.collect {|watcher| "#{watcher.name}.json"}
62
64
  files_to_purge = present_files.select {|svc| not managed_files.include?(svc)}
@@ -1,12 +1,15 @@
1
+ require 'synapse/config_generator/base'
2
+
1
3
  require 'fileutils'
2
4
  require 'json'
3
5
  require 'socket'
4
6
  require 'digest/sha1'
5
7
 
6
- module Synapse
7
- class Haproxy
8
- include Logging
9
- attr_reader :opts
8
+ class Synapse::ConfigGenerator
9
+ class Haproxy < BaseGenerator
10
+ include Synapse::Logging
11
+
12
+ NAME = 'haproxy'.freeze
10
13
 
11
14
  # these come from the documentation for haproxy (1.5 and 1.6)
12
15
  # http://haproxy.1wt.eu/download/1.5/doc/configuration.txt
@@ -790,31 +793,31 @@ module Synapse
790
793
 
791
794
  DEFAULT_STATE_FILE_TTL = (60 * 60 * 24).freeze # 24 hours
792
795
  STATE_FILE_UPDATE_INTERVAL = 60.freeze # iterations; not a unit of time
796
+ DEFAULT_BIND_ADDRESS = 'localhost'
793
797
 
794
798
  def initialize(opts)
795
- super()
799
+ super(opts)
796
800
 
797
- %w{global defaults reload_command}.each do |req|
801
+ %w{global defaults}.each do |req|
798
802
  raise ArgumentError, "haproxy requires a #{req} section" if !opts.has_key?(req)
799
803
  end
800
804
 
805
+ @opts['do_writes'] = true unless @opts.key?('do_writes')
806
+ @opts['do_socket'] = true unless @opts.key?('do_socket')
807
+ @opts['do_reloads'] = true unless @opts.key?('do_reloads')
808
+
801
809
  req_pairs = {
802
810
  'do_writes' => 'config_file_path',
803
811
  'do_socket' => 'socket_file_path',
804
- 'do_reloads' => 'reload_command'}
812
+ 'do_reloads' => 'reload_command'
813
+ }
805
814
 
806
815
  req_pairs.each do |cond, req|
807
- if opts[cond]
808
- raise ArgumentError, "the `#{req}` option is required when `#{cond}` is true" unless opts[req]
816
+ if @opts[cond]
817
+ raise ArgumentError, "the `#{req}` option is required when `#{cond}` is true" unless @opts[req]
809
818
  end
810
819
  end
811
820
 
812
- @opts = opts
813
-
814
- @opts['do_writes'] = true unless @opts.key?('do_writes')
815
- @opts['do_socket'] = true unless @opts.key?('do_socket')
816
- @opts['do_reloads'] = true unless @opts.key?('do_reloads')
817
-
818
821
  # socket_file_path can be a string or a list
819
822
  # lets make a new option which is always a list (plural)
820
823
  @opts['socket_file_paths'] = [@opts['socket_file_path']].flatten
@@ -835,8 +838,19 @@ module Synapse
835
838
  @state_file_ttl = @opts.fetch('state_file_ttl', DEFAULT_STATE_FILE_TTL).to_i
836
839
  end
837
840
 
838
- def name
839
- 'haproxy'
841
+ def normalize_watcher_provided_config(service_watcher_name, service_watcher_config)
842
+ service_watcher_config = super(service_watcher_name, service_watcher_config)
843
+ defaults = {
844
+ 'server_options' => "",
845
+ 'server_port_override' => nil,
846
+ 'backend' => [],
847
+ 'frontend' => [],
848
+ 'listen' => [],
849
+ }
850
+ unless service_watcher_config.include?('port')
851
+ log.warn "synapse: service #{service_watcher_name}: haproxy config does not include a port; only backend sections for the service will be created; you must move traffic there manually using configuration in `extra_sections`"
852
+ end
853
+ defaults.merge(service_watcher_config)
840
854
  end
841
855
 
842
856
  def tick(watchers)
@@ -848,13 +862,13 @@ module Synapse
848
862
 
849
863
  # We potentially have to restart if the restart was rate limited
850
864
  # in the original call to update_config
851
- restart if @opts['do_reloads'] && @restart_required
865
+ restart if opts['do_reloads'] && @restart_required
852
866
  end
853
867
 
854
868
  def update_config(watchers)
855
869
  # if we support updating backends, try that whenever possible
856
- if @opts['do_socket']
857
- @opts['socket_file_paths'].each do |socket_path|
870
+ if opts['do_socket']
871
+ opts['socket_file_paths'].each do |socket_path|
858
872
  update_backends_at(socket_path, watchers)
859
873
  end
860
874
  else
@@ -865,9 +879,9 @@ module Synapse
865
879
  new_config = generate_config(watchers)
866
880
 
867
881
  # if we write config files, lets do that and then possibly restart
868
- if @opts['do_writes']
882
+ if opts['do_writes']
869
883
  write_config(new_config)
870
- restart if @opts['do_reloads'] && @restart_required
884
+ restart if opts['do_reloads'] && @restart_required
871
885
  end
872
886
  end
873
887
 
@@ -877,14 +891,19 @@ module Synapse
877
891
  shared_frontend_lines = generate_shared_frontend
878
892
 
879
893
  watchers.each do |watcher|
894
+ watcher_config = watcher.config_for_generator[name]
880
895
  @watcher_configs[watcher.name] ||= parse_watcher_config(watcher)
896
+ next if watcher_config['disabled']
881
897
  new_config << generate_frontend_stanza(watcher, @watcher_configs[watcher.name]['frontend'])
882
898
  new_config << generate_backend_stanza(watcher, @watcher_configs[watcher.name]['backend'])
883
- if watcher.haproxy.include?('shared_frontend')
884
- if @opts['shared_frontend'] == nil
899
+ if watcher_config.include?('shared_frontend')
900
+ if opts['shared_frontend'] == nil
885
901
  log.warn "synapse: service #{watcher.name} contains a shared frontend section but the base config does not! skipping."
886
902
  else
887
- shared_frontend_lines << validate_haproxy_stanza(watcher.haproxy['shared_frontend'].map{|l| "\t#{l}"}, "frontend", "shared frontend section for #{watcher.name}")
903
+ tabbed_shared_frontend = watcher_config['shared_frontend'].map{|l| "\t#{l}"}
904
+ shared_frontend_lines << validate_haproxy_stanza(
905
+ tabbed_shared_frontend, "frontend", "shared frontend section for #{watcher.name}"
906
+ )
888
907
  end
889
908
  end
890
909
  end
@@ -896,10 +915,10 @@ module Synapse
896
915
 
897
916
  # pull out the shared frontend section if any
898
917
  def generate_shared_frontend
899
- return nil unless @opts.include?('shared_frontend')
918
+ return nil unless opts.include?('shared_frontend')
900
919
  log.debug "synapse: found a shared frontend section"
901
920
  shared_frontend_lines = ["\nfrontend shared-frontend"]
902
- shared_frontend_lines << validate_haproxy_stanza(@opts['shared_frontend'].map{|l| "\t#{l}"}, "frontend", "shared frontend")
921
+ shared_frontend_lines << validate_haproxy_stanza(opts['shared_frontend'].map{|l| "\t#{l}"}, "frontend", "shared frontend")
903
922
  return shared_frontend_lines
904
923
  end
905
924
 
@@ -909,13 +928,13 @@ module Synapse
909
928
 
910
929
  %w{global defaults}.each do |section|
911
930
  base_config << "#{section}"
912
- @opts[section].each do |option|
931
+ opts[section].each do |option|
913
932
  base_config << "\t#{option}"
914
933
  end
915
934
  end
916
935
 
917
- if @opts['extra_sections']
918
- @opts['extra_sections'].each do |title, section|
936
+ if opts['extra_sections']
937
+ opts['extra_sections'].each do |title, section|
919
938
  base_config << "\n#{title}"
920
939
  section.each do |option|
921
940
  base_config << "\t#{option}"
@@ -930,12 +949,13 @@ module Synapse
930
949
  # frontend and backend sections
931
950
  def parse_watcher_config(watcher)
932
951
  config = {}
952
+ watcher_config = watcher.config_for_generator[name]
933
953
  %w{frontend backend}.each do |section|
934
- config[section] = watcher.haproxy[section] || []
954
+ config[section] = watcher_config[section] || []
935
955
 
936
956
  # copy over the settings from the 'listen' section that pertain to section
937
957
  config[section].concat(
938
- watcher.haproxy['listen'].select {|setting|
958
+ watcher_config['listen'].select {|setting|
939
959
  parsed_setting = setting.strip.gsub(/\s+/, ' ').downcase
940
960
  SECTION_FIELDS[section].any? {|field| parsed_setting.start_with?(field)}
941
961
  })
@@ -961,16 +981,33 @@ module Synapse
961
981
 
962
982
  # generates an individual stanza for a particular watcher
963
983
  def generate_frontend_stanza(watcher, config)
964
- unless watcher.haproxy.has_key?("port")
984
+ watcher_config = watcher.config_for_generator[name]
985
+ unless watcher_config.has_key?("port")
965
986
  log.debug "synapse: not generating frontend stanza for watcher #{watcher.name} because it has no port defined"
966
987
  return []
988
+ else
989
+ port = watcher_config['port']
967
990
  end
968
991
 
992
+
993
+ bind_address = (
994
+ watcher_config['bind_address'] ||
995
+ opts['bind_address'] ||
996
+ DEFAULT_BIND_ADDRESS
997
+ )
998
+ backend_name = watcher_config.fetch('backend_name', watcher.name)
999
+
1000
+ bind_line = [
1001
+ "\tbind",
1002
+ "#{bind_address}:#{port}",
1003
+ watcher_config['bind_options']
1004
+ ].compact.join(' ')
1005
+
969
1006
  stanza = [
970
1007
  "\nfrontend #{watcher.name}",
971
1008
  config.map {|c| "\t#{c}"},
972
- "\tbind #{ watcher.haproxy['bind_address'] || @opts['bind_address'] || 'localhost'}:#{watcher.haproxy['port']}",
973
- "\tdefault_backend #{watcher.haproxy.fetch('backend_name', watcher.name)}"
1009
+ bind_line,
1010
+ "\tdefault_backend #{backend_name}"
974
1011
  ]
975
1012
  end
976
1013
 
@@ -1004,7 +1041,8 @@ module Synapse
1004
1041
  log.debug "synapse: no backends found for watcher #{watcher.name}"
1005
1042
  end
1006
1043
 
1007
- keys = case watcher.haproxy['backend_order']
1044
+ watcher_config = watcher.config_for_generator[name]
1045
+ keys = case watcher_config['backend_order']
1008
1046
  when 'asc'
1009
1047
  backends.keys.sort
1010
1048
  when 'desc'
@@ -1016,20 +1054,20 @@ module Synapse
1016
1054
  end
1017
1055
 
1018
1056
  stanza = [
1019
- "\nbackend #{watcher.haproxy.fetch('backend_name', watcher.name)}",
1057
+ "\nbackend #{watcher_config.fetch('backend_name', watcher.name)}",
1020
1058
  config.map {|c| "\t#{c}"},
1021
1059
  keys.map {|backend_name|
1022
1060
  backend = backends[backend_name]
1023
1061
  b = "\tserver #{backend_name} #{backend['host']}:#{backend['port']}"
1024
1062
  unless config.include?('mode tcp')
1025
- b = case watcher.haproxy['cookie_value_method']
1063
+ b = case watcher_config['cookie_value_method']
1026
1064
  when 'hash'
1027
1065
  b = "#{b} cookie #{Digest::SHA1.hexdigest(backend_name)}"
1028
1066
  else
1029
1067
  b = "#{b} cookie #{backend_name}"
1030
1068
  end
1031
1069
  end
1032
- b = "#{b} #{watcher.haproxy['server_options']}" if watcher.haproxy['server_options']
1070
+ b = "#{b} #{watcher_config['server_options']}" if watcher_config['server_options']
1033
1071
  b = "#{b} #{backend['haproxy_server_options']}" if backend['haproxy_server_options']
1034
1072
  b = "#{b} disabled" unless backend['enabled']
1035
1073
  b }
@@ -1075,6 +1113,7 @@ module Synapse
1075
1113
  watchers.each do |watcher|
1076
1114
  enabled_backends[watcher.name] = []
1077
1115
  next if watcher.backends.empty?
1116
+ next if watcher.config_for_generator[name]['disabled']
1078
1117
 
1079
1118
  unless cur_backends.include? watcher.name
1080
1119
  log.info "synapse: restart required because we added new section #{watcher.name}"
@@ -1125,16 +1164,16 @@ module Synapse
1125
1164
  # writes the config
1126
1165
  def write_config(new_config)
1127
1166
  begin
1128
- old_config = File.read(@opts['config_file_path'])
1167
+ old_config = File.read(opts['config_file_path'])
1129
1168
  rescue Errno::ENOENT => e
1130
- log.info "synapse: could not open haproxy config file at #{@opts['config_file_path']}"
1169
+ log.info "synapse: could not open haproxy config file at #{opts['config_file_path']}"
1131
1170
  old_config = ""
1132
1171
  end
1133
1172
 
1134
1173
  if old_config == new_config
1135
1174
  return false
1136
1175
  else
1137
- File.open(@opts['config_file_path'],'w') {|f| f.write(new_config)}
1176
+ File.open(opts['config_file_path'],'w') {|f| f.write(new_config)}
1138
1177
  return true
1139
1178
  end
1140
1179
  end