synapse 0.12.2 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +37 -11
- data/lib/synapse/haproxy.rb +12 -2
- data/lib/synapse/service_watcher/base.rb +14 -0
- data/lib/synapse/service_watcher/zookeeper.rb +79 -7
- data/lib/synapse/version.rb +1 -1
- data/spec/lib/synapse/haproxy_spec.rb +54 -0
- data/spec/lib/synapse/service_watcher_base_spec.rb +26 -0
- data/spec/lib/synapse/service_watcher_zookeeper_spec.rb +47 -0
- data/synapse.gemspec +2 -1
- metadata +47 -49
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 34b7877f7f8196407e10fafab1a72354e861121c
|
4
|
+
data.tar.gz: 154d642f85c7a1d1ab13078a8adb1a75d295d993
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 06e858b8cd8a0c316baad80264754206ee64615f7ca0f7958ebf7d38a5b07d67968d949d09367dd127b525e1f6710611d48813af224fe3371bd1e2c78c4da343
|
7
|
+
data.tar.gz: 41be259d76cd50f950b4d6e2e217f4c68134a8f71cfcc75bf69842f7d5edee95bac61996412ba8b9a00057ceb52e651aa4837e2146c3c57432929fbbbfb1f641
|
data/README.md
CHANGED
@@ -97,6 +97,11 @@ install synapse with:
|
|
97
97
|
|
98
98
|
```bash
|
99
99
|
$ mkdir -p /opt/smartstack/synapse
|
100
|
+
|
101
|
+
# If you want to install specific versions of dependencies such as an older
|
102
|
+
# version of the aws-sdk, the docker-api, etc, gem install that here *before*
|
103
|
+
# gem installing synapse
|
104
|
+
|
100
105
|
# If you are on Ruby 2.X use --no-document instead of --no-ri --no-rdoc
|
101
106
|
$ gem install synapse --install-dir /opt/smartstack/synapse --no-ri --no-rdoc
|
102
107
|
```
|
@@ -141,25 +146,39 @@ Each value in the services hash is also a hash, and should contain the following
|
|
141
146
|
We've included a number of `watchers` which provide service discovery.
|
142
147
|
Put these into the `discovery` section of the service hash, with these options:
|
143
148
|
|
144
|
-
#####
|
149
|
+
##### Base #####
|
145
150
|
|
146
|
-
The
|
151
|
+
The base watcher is useful in situations where you only want to use the servers in the `default_servers` list.
|
147
152
|
It has only one option:
|
148
153
|
|
149
|
-
* `method`:
|
154
|
+
* `method`: base
|
150
155
|
|
151
156
|
##### Zookeeper #####
|
152
157
|
|
153
158
|
This watcher retrieves a list of servers from zookeeper.
|
154
|
-
It takes the following
|
159
|
+
It takes the following mandatory arguments:
|
155
160
|
|
156
161
|
* `method`: zookeeper
|
157
162
|
* `path`: the zookeeper path where ephemeral nodes will be created for each available service server
|
158
163
|
* `hosts`: the list of zookeeper servers to query
|
159
164
|
|
160
165
|
The watcher assumes that each node under `path` represents a service server.
|
161
|
-
|
162
|
-
|
166
|
+
|
167
|
+
The following arguments are optional:
|
168
|
+
|
169
|
+
* `decode`: A hash containing configuration for how to decode the data found in zookeeper.
|
170
|
+
|
171
|
+
###### Decoding service nodes ######
|
172
|
+
Synapse attempts to decode the data in each of these nodes using JSON and you can control how it is decoded with the `decode` argument. If provided, the `decode` hash should contain the following:
|
173
|
+
|
174
|
+
* `method` (one of ['`nerve`', '`serverset`'], default: '`nerve`'): The kind of data to expect to find in zookeeper nodes
|
175
|
+
* `endpoint_name` (default: nil): If using the `serverset` method, this controls which of the `additionalEndpoints` is chosen instead of the `serviceEndpoint` data. If not supplied the `serverset` method will use the host/port from the `serviceEndpoint` data.
|
176
|
+
|
177
|
+
If the `method` is `nerve`, then we expect to find nerve registrations with a `host` and a `port`.
|
178
|
+
|
179
|
+
If the `method` is `serverset` then we expect to find Finagle ServerSet
|
180
|
+
(also used by [Aurora](https://github.com/apache/aurora/blob/master/docs/user-guide.md#service-discovery)) registrations with a `serviceEndpoint` and optionally one or more `additionalEndpoints`.
|
181
|
+
The Synapse `name` will be automatically deduced from `shard` if present.
|
163
182
|
|
164
183
|
##### Docker #####
|
165
184
|
|
@@ -232,6 +251,7 @@ by unsetting `use_previous_backends`.
|
|
232
251
|
This section is its own hash, which should contain the following keys:
|
233
252
|
|
234
253
|
* `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`
|
254
|
+
* `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.
|
235
255
|
* `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.
|
236
256
|
* `server_options`: the haproxy options for each `server` line of the service in HAProxy config; it may be left out.
|
237
257
|
* `frontend`: additional lines passed to the HAProxy config in the `frontend` stanza of this service
|
@@ -239,6 +259,7 @@ This section is its own hash, which should contain the following keys:
|
|
239
259
|
* `backend_name`: The name of the generated HAProxy backend for this service
|
240
260
|
(defaults to the service's key in the `services` section)
|
241
261
|
* `listen`: these lines will be parsed and placed in the correct `frontend`/`backend` section as applicable; you can put lines which are the same for the frontend and backend here.
|
262
|
+
* `backend_order`: optional: how backends should be ordered in the `backend` stanza. (default is shuffling). Setting to `asc` means sorting backends in ascending alphabetical order before generating stanza. `desc` means descending alphabetical order. `no_shuffle` means no shuffling or sorting.
|
242
263
|
* `shared_frontend`: optional: haproxy configuration directives for a shared http frontend (see below)
|
243
264
|
|
244
265
|
<a name="haproxy"/>
|
@@ -259,11 +280,16 @@ The top level `haproxy` section of the config file has the following options:
|
|
259
280
|
* `restart_interval`: number of seconds to wait between restarts of haproxy (default: 2)
|
260
281
|
* `restart_jitter`: percentage, expressed as a float, of jitter to multiply the `restart_interval` by when determining the next
|
261
282
|
restart time. Use this to help prevent healthcheck storms when HAProxy restarts. (default: 0.0)
|
262
|
-
* `state_file_path`: full path on disk (e.g. /tmp/synapse/state.json) for
|
263
|
-
If provided, synapse will store
|
264
|
-
|
265
|
-
|
266
|
-
|
283
|
+
* `state_file_path`: full path on disk (e.g. /tmp/synapse/state.json) for
|
284
|
+
caching haproxy state between reloads. If provided, synapse will store
|
285
|
+
recently seen backends at this location and can "remember" backends across
|
286
|
+
both synapse and HAProxy restarts. Any backends that are "down" in the
|
287
|
+
reporter but listed in the cache will be put into HAProxy disabled. Synapse
|
288
|
+
writes the state file every sixty seconds, so the file's age can be used to
|
289
|
+
monitor that Synapse is alive and making progress. (default: nil)
|
290
|
+
* `state_file_ttl`: the number of seconds that backends should be kept in the
|
291
|
+
state file cache. This only applies if `state_file_path` is provided.
|
292
|
+
(default: 86400)
|
267
293
|
|
268
294
|
Note that a non-default `bind_address` can be dangerous.
|
269
295
|
If you configure an `address:port` combination that is already in use on the system, haproxy will fail to start.
|
data/lib/synapse/haproxy.rb
CHANGED
@@ -686,7 +686,7 @@ module Synapse
|
|
686
686
|
stanza = [
|
687
687
|
"\nfrontend #{watcher.name}",
|
688
688
|
config.map {|c| "\t#{c}"},
|
689
|
-
"\tbind #{@opts['bind_address'] || 'localhost'}:#{watcher.haproxy['port']}",
|
689
|
+
"\tbind #{ watcher.haproxy['bind_address'] || @opts['bind_address'] || 'localhost'}:#{watcher.haproxy['port']}",
|
690
690
|
"\tdefault_backend #{watcher.haproxy.fetch('backend_name', watcher.name)}"
|
691
691
|
]
|
692
692
|
end
|
@@ -721,10 +721,20 @@ module Synapse
|
|
721
721
|
log.debug "synapse: no backends found for watcher #{watcher.name}"
|
722
722
|
end
|
723
723
|
|
724
|
+
keys = case watcher.haproxy['backend_order']
|
725
|
+
when 'asc'
|
726
|
+
backends.keys.sort
|
727
|
+
when 'desc'
|
728
|
+
backends.keys.sort.reverse
|
729
|
+
when 'no_shuffle'
|
730
|
+
backends.keys
|
731
|
+
else
|
732
|
+
backends.keys.shuffle
|
733
|
+
end
|
724
734
|
stanza = [
|
725
735
|
"\nbackend #{watcher.haproxy.fetch('backend_name', watcher.name)}",
|
726
736
|
config.map {|c| "\t#{c}"},
|
727
|
-
|
737
|
+
keys.map {|backend_name|
|
728
738
|
backend = backends[backend_name]
|
729
739
|
b = "\tserver #{backend_name} #{backend['host']}:#{backend['port']}"
|
730
740
|
b = "#{b} cookie #{backend_name}" unless config.include?('mode tcp')
|
@@ -21,6 +21,7 @@ class Synapse::ServiceWatcher
|
|
21
21
|
|
22
22
|
@name = opts['name']
|
23
23
|
@discovery = opts['discovery']
|
24
|
+
@label_filter = @discovery['label_filter'] || false
|
24
25
|
|
25
26
|
@leader_election = opts['leader_election'] || false
|
26
27
|
@leader_last_warn = Time.now - LEADER_WARN_INTERVAL
|
@@ -86,6 +87,8 @@ class Synapse::ServiceWatcher
|
|
86
87
|
|
87
88
|
# if leader election fails, return no backends
|
88
89
|
return []
|
90
|
+
elsif @label_filter
|
91
|
+
return filter_backends_by_label(@backends, @label_filter)
|
89
92
|
end
|
90
93
|
|
91
94
|
return @backends
|
@@ -99,6 +102,17 @@ class Synapse::ServiceWatcher
|
|
99
102
|
log.warn "synapse: warning: a stub watcher with no default servers is pretty useless" if @default_servers.empty?
|
100
103
|
end
|
101
104
|
|
105
|
+
def filter_backends_by_label(backends, label_filter)
|
106
|
+
filtered_backends = []
|
107
|
+
backends.each do |backend|
|
108
|
+
backend_labels = backend['labels'] || {}
|
109
|
+
if label_filter['condition'] == 'equals' and backend_labels[label_filter['label']] == label_filter['value']
|
110
|
+
filtered_backends << backend
|
111
|
+
end
|
112
|
+
end
|
113
|
+
return filtered_backends
|
114
|
+
end
|
115
|
+
|
102
116
|
def set_backends(new_backends)
|
103
117
|
# Aggregate and deduplicate all potential backend service instances.
|
104
118
|
new_backends = (new_backends + @default_servers) if @keep_default_servers
|
@@ -11,6 +11,24 @@ class Synapse::ServiceWatcher
|
|
11
11
|
@@zk_pool_count = {}
|
12
12
|
@@zk_pool_lock = Mutex.new
|
13
13
|
|
14
|
+
def initialize(opts={}, synapse)
|
15
|
+
super(opts, synapse)
|
16
|
+
|
17
|
+
# Alternative deserialization support. By default we use nerve
|
18
|
+
# deserialization, but we also support serverset registries
|
19
|
+
@decode_method = self.method(:nerve_decode)
|
20
|
+
if @discovery['decode']
|
21
|
+
valid_methods = ['nerve', 'serverset']
|
22
|
+
decode_method = @discovery['decode']['method']
|
23
|
+
unless decode_method && valid_methods.include?(decode_method)
|
24
|
+
raise ArgumentError, "missing or invalid decode method #{decode_method}"
|
25
|
+
end
|
26
|
+
if decode_method == 'serverset'
|
27
|
+
@decode_method = self.method(:serverset_decode)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
14
32
|
def start
|
15
33
|
@zk_hosts = @discovery['hosts'].sort.join(',')
|
16
34
|
|
@@ -43,6 +61,57 @@ class Synapse::ServiceWatcher
|
|
43
61
|
unless @discovery['path']
|
44
62
|
end
|
45
63
|
|
64
|
+
# Supported decode methods
|
65
|
+
|
66
|
+
# Airbnb nerve ZK node data looks like this:
|
67
|
+
#
|
68
|
+
# {
|
69
|
+
# "host": "somehostname",
|
70
|
+
# "port": 1234,
|
71
|
+
# }
|
72
|
+
def nerve_decode(data)
|
73
|
+
JSON.parse(data)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Twitter serverset ZK node data looks like this:
|
77
|
+
#
|
78
|
+
# {
|
79
|
+
# "additionalEndpoints": {
|
80
|
+
# "serverset": {
|
81
|
+
# "host": "somehostname",
|
82
|
+
# "port": 31943
|
83
|
+
# },
|
84
|
+
# "http": {
|
85
|
+
# "host": "somehostname",
|
86
|
+
# "port": 31943
|
87
|
+
# },
|
88
|
+
# "otherport": {
|
89
|
+
# "host": "somehostname",
|
90
|
+
# "port": 31944
|
91
|
+
# }
|
92
|
+
# },
|
93
|
+
# "serviceEndpoint": {
|
94
|
+
# "host": "somehostname",
|
95
|
+
# "port": 31943
|
96
|
+
# },
|
97
|
+
# "shard": 0,
|
98
|
+
# "status": "ALIVE"
|
99
|
+
# }
|
100
|
+
def serverset_decode(data)
|
101
|
+
decoded = JSON.parse(data)
|
102
|
+
if @discovery['decode']['endpoint_name']
|
103
|
+
endpoint_name = @discovery['decode']['endpoint_name']
|
104
|
+
raise KeyError, "json data has no additionalEndpoint called #{endpoint_name}" \
|
105
|
+
unless decoded['additionalEndpoints'] && decoded['additionalEndpoints'][endpoint_name]
|
106
|
+
result = decoded['additionalEndpoints'][endpoint_name]
|
107
|
+
else
|
108
|
+
result = decoded['serviceEndpoint']
|
109
|
+
end
|
110
|
+
result['name'] = decoded['shard'] || nil
|
111
|
+
result['name'] = result['name'].to_s unless result['name'].nil?
|
112
|
+
result
|
113
|
+
end
|
114
|
+
|
46
115
|
# helper method that ensures that the discovery path exists
|
47
116
|
def create(path)
|
48
117
|
log.debug "synapse: creating ZK path: #{path}"
|
@@ -62,11 +131,11 @@ class Synapse::ServiceWatcher
|
|
62
131
|
|
63
132
|
begin
|
64
133
|
# TODO: Do less munging, or refactor out this processing
|
65
|
-
host, port, name, weight, haproxy_server_options = deserialize_service_instance(node.first)
|
134
|
+
host, port, name, weight, haproxy_server_options, labels = deserialize_service_instance(node.first)
|
66
135
|
rescue StandardError => e
|
67
136
|
log.error "synapse: invalid data in ZK node #{id} at #{@discovery['path']}: #{e}"
|
68
137
|
else
|
69
|
-
server_port = @server_port_override ? @server_port_override : port
|
138
|
+
server_port = @haproxy['server_port_override'] ? @haproxy['server_port_override'] : port
|
70
139
|
|
71
140
|
# find the numberic id in the node name; used for leader elections if enabled
|
72
141
|
numeric_id = id.split('_').last
|
@@ -76,7 +145,8 @@ class Synapse::ServiceWatcher
|
|
76
145
|
new_backends << {
|
77
146
|
'name' => name, 'host' => host, 'port' => server_port,
|
78
147
|
'id' => numeric_id, 'weight' => weight,
|
79
|
-
'haproxy_server_options' => haproxy_server_options
|
148
|
+
'haproxy_server_options' => haproxy_server_options,
|
149
|
+
'labels' => labels
|
80
150
|
}
|
81
151
|
end
|
82
152
|
end
|
@@ -173,15 +243,17 @@ class Synapse::ServiceWatcher
|
|
173
243
|
# decode the data at a zookeeper endpoint
|
174
244
|
def deserialize_service_instance(data)
|
175
245
|
log.debug "synapse: deserializing process data"
|
176
|
-
decoded =
|
246
|
+
decoded = @decode_method.call(data)
|
177
247
|
|
178
|
-
host = decoded['host'] || (raise
|
179
|
-
port = decoded['port'] || (raise
|
248
|
+
host = decoded['host'] || (raise KeyError, 'instance json data does not have host key')
|
249
|
+
port = decoded['port'] || (raise KeyError, 'instance json data does not have port key')
|
180
250
|
name = decoded['name'] || nil
|
181
251
|
weight = decoded['weight'] || nil
|
182
252
|
haproxy_server_options = decoded['haproxy_server_options'] || nil
|
253
|
+
labels = decoded['labels'] || nil
|
183
254
|
|
184
|
-
return host, port, name, weight, haproxy_server_options
|
255
|
+
return host, port, name, weight, haproxy_server_options, labels
|
185
256
|
end
|
186
257
|
end
|
187
258
|
end
|
259
|
+
|
data/lib/synapse/version.rb
CHANGED
@@ -23,6 +23,21 @@ describe Synapse::Haproxy do
|
|
23
23
|
mockWatcher
|
24
24
|
end
|
25
25
|
|
26
|
+
let(:mockwatcher_frontend) do
|
27
|
+
mockWatcher = double(Synapse::ServiceWatcher)
|
28
|
+
allow(mockWatcher).to receive(:name).and_return('example_service')
|
29
|
+
allow(mockWatcher).to receive(:haproxy).and_return('port' => 2200)
|
30
|
+
mockWatcher
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:mockwatcher_frontend_with_bind_address) do
|
34
|
+
mockWatcher = double(Synapse::ServiceWatcher)
|
35
|
+
allow(mockWatcher).to receive(:name).and_return('example_service')
|
36
|
+
allow(mockWatcher).to receive(:haproxy).and_return('port' => 2200, 'bind_address' => "127.0.0.3")
|
37
|
+
mockWatcher
|
38
|
+
end
|
39
|
+
|
40
|
+
|
26
41
|
it 'updating the config' do
|
27
42
|
expect(subject).to receive(:generate_config)
|
28
43
|
subject.update_config([mockwatcher])
|
@@ -33,6 +48,34 @@ describe Synapse::Haproxy do
|
|
33
48
|
expect(subject.generate_backend_stanza(mockwatcher, mockConfig)).to eql(["\nbackend example_service", [], ["\tserver somehost:5555 somehost:5555 cookie somehost:5555 check inter 2000 rise 3 fall 2"]])
|
34
49
|
end
|
35
50
|
|
51
|
+
describe 'generate backend stanza in correct order' do
|
52
|
+
let(:multiple_backends_stanza_map) do
|
53
|
+
{
|
54
|
+
'asc' => ["\nbackend example_service", [], ["\tserver somehost1:5555 somehost1:5555 cookie somehost1:5555 check inter 2000 rise 3 fall 2", "\tserver somehost2:5555 somehost2:5555 cookie somehost2:5555 check inter 2000 rise 3 fall 2", "\tserver somehost3:5555 somehost3:5555 cookie somehost3:5555 check inter 2000 rise 3 fall 2"]],
|
55
|
+
'desc' => ["\nbackend example_service", [], ["\tserver somehost3:5555 somehost3:5555 cookie somehost3:5555 check inter 2000 rise 3 fall 2", "\tserver somehost2:5555 somehost2:5555 cookie somehost2:5555 check inter 2000 rise 3 fall 2", "\tserver somehost1:5555 somehost1:5555 cookie somehost1:5555 check inter 2000 rise 3 fall 2"]],
|
56
|
+
'no_shuffle' => ["\nbackend example_service", [], ["\tserver somehost1:5555 somehost1:5555 cookie somehost1:5555 check inter 2000 rise 3 fall 2", "\tserver somehost3:5555 somehost3:5555 cookie somehost3:5555 check inter 2000 rise 3 fall 2", "\tserver somehost2:5555 somehost2:5555 cookie somehost2:5555 check inter 2000 rise 3 fall 2"]]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:mockwatcher_with_multiple_backends) do
|
61
|
+
mockWatcher = double(Synapse::ServiceWatcher)
|
62
|
+
allow(mockWatcher).to receive(:name).and_return('example_service')
|
63
|
+
backends = [{ 'host' => 'somehost1', 'port' => 5555}, {'host' => 'somehost3', 'port' => 5555}, { 'host' => 'somehost2', 'port' => 5555}]
|
64
|
+
allow(mockWatcher).to receive(:backends).and_return(backends)
|
65
|
+
mockWatcher
|
66
|
+
end
|
67
|
+
|
68
|
+
['asc', 'desc', 'no_shuffle'].each do |order_option|
|
69
|
+
context "when #{order_option} is specified for backend_order" do
|
70
|
+
it 'generates backend stanza in correct order' do
|
71
|
+
mockConfig = []
|
72
|
+
allow(mockwatcher_with_multiple_backends).to receive(:haproxy).and_return({'server_options' => "check inter 2000 rise 3 fall 2", 'backend_order' => order_option})
|
73
|
+
expect(subject.generate_backend_stanza(mockwatcher_with_multiple_backends, mockConfig)).to eql(multiple_backends_stanza_map[order_option])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
36
79
|
it 'generates backend stanza without cookies for tcp mode' do
|
37
80
|
mockConfig = ['mode tcp']
|
38
81
|
expect(subject.generate_backend_stanza(mockwatcher, mockConfig)).to eql(["\nbackend example_service", ["\tmode tcp"], ["\tserver somehost:5555 somehost:5555 check inter 2000 rise 3 fall 2"]])
|
@@ -42,4 +85,15 @@ describe Synapse::Haproxy do
|
|
42
85
|
mockConfig = []
|
43
86
|
expect(subject.generate_backend_stanza(mockwatcher_with_server_options, mockConfig)).to eql(["\nbackend example_service", [], ["\tserver somehost:5555 somehost:5555 cookie somehost:5555 check inter 2000 rise 3 fall 2 backup"]])
|
44
87
|
end
|
88
|
+
|
89
|
+
it 'generates frontend stanza ' do
|
90
|
+
mockConfig = []
|
91
|
+
expect(subject.generate_frontend_stanza(mockwatcher_frontend, mockConfig)).to eql(["\nfrontend example_service", [], "\tbind localhost:2200", "\tdefault_backend example_service"])
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'respects frontend bind_address ' do
|
95
|
+
mockConfig = []
|
96
|
+
expect(subject.generate_frontend_stanza(mockwatcher_frontend_with_bind_address, mockConfig)).to eql(["\nfrontend example_service", [], "\tbind 127.0.0.3:2200", "\tdefault_backend example_service"])
|
97
|
+
end
|
98
|
+
|
45
99
|
end
|
@@ -109,5 +109,31 @@ describe Synapse::ServiceWatcher::BaseWatcher do
|
|
109
109
|
expect(subject.backends).to eq(backends + default_servers)
|
110
110
|
end
|
111
111
|
end
|
112
|
+
|
113
|
+
context 'with label_filter set' do
|
114
|
+
let(:matching_labeled_backends) { [
|
115
|
+
{ 'name' => 'server1', 'host' => 'server1', 'port' => 1111, 'labels' => { 'az' => 'us-east-1a' } },
|
116
|
+
{ 'name' => 'server2', 'host' => 'server2', 'port' => 2222, 'labels' => { 'az' => 'us-east-1a' } },
|
117
|
+
] }
|
118
|
+
let(:non_matching_labeled_backends) { [
|
119
|
+
{ 'name' => 'server3', 'host' => 'server3', 'port' => 3333, 'labels' => { 'az' => 'us-west-1c' } },
|
120
|
+
{ 'name' => 'server4', 'host' => 'server4', 'port' => 4444, 'labels' => { 'az' => 'us-west-2a' } },
|
121
|
+
] }
|
122
|
+
let(:non_labeled_backends) { [
|
123
|
+
{ 'name' => 'server5', 'host' => 'server5', 'port' => 5555 },
|
124
|
+
] }
|
125
|
+
let(:args) {
|
126
|
+
testargs.merge({ 'discovery' => {
|
127
|
+
'method' => 'base',
|
128
|
+
'label_filter' => { 'condition' => 'equals', 'label' => 'az', 'value' => 'us-east-1a' } }
|
129
|
+
})
|
130
|
+
}
|
131
|
+
it 'removes all backends that do not match the label_filter' do
|
132
|
+
expect(subject).to receive(:'reconfigure!').exactly(:once)
|
133
|
+
subject.send(:set_backends, matching_labeled_backends + non_matching_labeled_backends +
|
134
|
+
non_labeled_backends)
|
135
|
+
expect(subject.backends).to eq(matching_labeled_backends)
|
136
|
+
end
|
137
|
+
end
|
112
138
|
end
|
113
139
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'synapse/service_watcher/zookeeper'
|
3
|
+
require 'synapse/service_watcher/zookeeper_dns'
|
4
|
+
|
5
|
+
describe Synapse::ServiceWatcher::ZookeeperWatcher do
|
6
|
+
let(:mock_synapse) { double }
|
7
|
+
let(:config) do
|
8
|
+
{
|
9
|
+
'name' => 'test',
|
10
|
+
'haproxy' => {},
|
11
|
+
'discovery' => discovery,
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:service_data) do
|
16
|
+
{
|
17
|
+
'host' => 'server',
|
18
|
+
'port' => '8888',
|
19
|
+
'name' => 'server',
|
20
|
+
'weight' => '1',
|
21
|
+
'haproxy_server_options' => 'backup',
|
22
|
+
'labels' => { 'az' => 'us-east-1a' }
|
23
|
+
}
|
24
|
+
end
|
25
|
+
let(:service_data_string) { service_data.to_json }
|
26
|
+
let(:deserialized_service_data) {
|
27
|
+
[ service_data['host'], service_data['port'], service_data['name'], service_data['weight'],
|
28
|
+
service_data['haproxy_server_options'], service_data['labels'] ]
|
29
|
+
}
|
30
|
+
|
31
|
+
context 'ZookeeperWatcher' do
|
32
|
+
let(:discovery) { { 'method' => 'zookeeper', 'hosts' => 'somehost','path' => 'some/path' } }
|
33
|
+
subject { Synapse::ServiceWatcher::ZookeeperWatcher.new(config, mock_synapse) }
|
34
|
+
it 'decodes data correctly' do
|
35
|
+
expect(subject.send(:deserialize_service_instance, service_data_string)).to eql(deserialized_service_data)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'ZookeeperDnsWatcher' do
|
40
|
+
let(:discovery) { { 'method' => 'zookeeper_dns', 'hosts' => 'somehost','path' => 'some/path' } }
|
41
|
+
let(:message_queue) { [] }
|
42
|
+
subject { Synapse::ServiceWatcher::ZookeeperDnsWatcher::Zookeeper.new(config, mock_synapse, message_queue) }
|
43
|
+
it 'decodes data correctly' do
|
44
|
+
expect(subject.send(:deserialize_service_instance, service_data_string)).to eql(deserialized_service_data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/synapse.gemspec
CHANGED
@@ -17,8 +17,9 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
|
19
19
|
gem.add_runtime_dependency "aws-sdk", "~> 1.39"
|
20
|
-
gem.add_runtime_dependency "docker-api", "~> 1.7
|
20
|
+
gem.add_runtime_dependency "docker-api", "~> 1.7"
|
21
21
|
gem.add_runtime_dependency "zk", "~> 1.9.4"
|
22
|
+
gem.add_runtime_dependency "logging", "~> 1.8"
|
22
23
|
|
23
24
|
gem.add_development_dependency "rake"
|
24
25
|
gem.add_development_dependency "rspec", "~> 3.1.0"
|
metadata
CHANGED
@@ -1,145 +1,142 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synapse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.13.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Martin Rhoads
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2016-02-18 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: aws-sdk
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '1.39'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- - ~>
|
24
|
+
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '1.39'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: docker-api
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- - ~>
|
31
|
+
- - "~>"
|
36
32
|
- !ruby/object:Gem::Version
|
37
|
-
version: 1.7
|
33
|
+
version: '1.7'
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- - ~>
|
38
|
+
- - "~>"
|
44
39
|
- !ruby/object:Gem::Version
|
45
|
-
version: 1.7
|
40
|
+
version: '1.7'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: zk
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- - ~>
|
45
|
+
- - "~>"
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: 1.9.4
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- - ~>
|
52
|
+
- - "~>"
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: 1.9.4
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: logging
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.8'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.8'
|
62
69
|
- !ruby/object:Gem::Dependency
|
63
70
|
name: rake
|
64
71
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
72
|
requirements:
|
67
|
-
- -
|
73
|
+
- - ">="
|
68
74
|
- !ruby/object:Gem::Version
|
69
75
|
version: '0'
|
70
76
|
type: :development
|
71
77
|
prerelease: false
|
72
78
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
79
|
requirements:
|
75
|
-
- -
|
80
|
+
- - ">="
|
76
81
|
- !ruby/object:Gem::Version
|
77
82
|
version: '0'
|
78
83
|
- !ruby/object:Gem::Dependency
|
79
84
|
name: rspec
|
80
85
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
86
|
requirements:
|
83
|
-
- - ~>
|
87
|
+
- - "~>"
|
84
88
|
- !ruby/object:Gem::Version
|
85
89
|
version: 3.1.0
|
86
90
|
type: :development
|
87
91
|
prerelease: false
|
88
92
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
93
|
requirements:
|
91
|
-
- - ~>
|
94
|
+
- - "~>"
|
92
95
|
- !ruby/object:Gem::Version
|
93
96
|
version: 3.1.0
|
94
97
|
- !ruby/object:Gem::Dependency
|
95
98
|
name: pry
|
96
99
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
100
|
requirements:
|
99
|
-
- -
|
101
|
+
- - ">="
|
100
102
|
- !ruby/object:Gem::Version
|
101
103
|
version: '0'
|
102
104
|
type: :development
|
103
105
|
prerelease: false
|
104
106
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
107
|
requirements:
|
107
|
-
- -
|
108
|
+
- - ">="
|
108
109
|
- !ruby/object:Gem::Version
|
109
110
|
version: '0'
|
110
111
|
- !ruby/object:Gem::Dependency
|
111
112
|
name: pry-nav
|
112
113
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
121
|
requirements:
|
123
|
-
- -
|
122
|
+
- - ">="
|
124
123
|
- !ruby/object:Gem::Version
|
125
124
|
version: '0'
|
126
125
|
- !ruby/object:Gem::Dependency
|
127
126
|
name: webmock
|
128
127
|
requirement: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
128
|
requirements:
|
131
|
-
- -
|
129
|
+
- - ">="
|
132
130
|
- !ruby/object:Gem::Version
|
133
131
|
version: '0'
|
134
132
|
type: :development
|
135
133
|
prerelease: false
|
136
134
|
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
none: false
|
138
135
|
requirements:
|
139
|
-
- -
|
136
|
+
- - ">="
|
140
137
|
- !ruby/object:Gem::Version
|
141
138
|
version: '0'
|
142
|
-
description:
|
139
|
+
description: ": Write a gem description"
|
143
140
|
email:
|
144
141
|
- martin.rhoads@airbnb.com
|
145
142
|
executables:
|
@@ -147,10 +144,10 @@ executables:
|
|
147
144
|
extensions: []
|
148
145
|
extra_rdoc_files: []
|
149
146
|
files:
|
150
|
-
- .gitignore
|
151
|
-
- .mailmap
|
152
|
-
- .rspec
|
153
|
-
- .travis.yml
|
147
|
+
- ".gitignore"
|
148
|
+
- ".mailmap"
|
149
|
+
- ".rspec"
|
150
|
+
- ".travis.yml"
|
154
151
|
- Gemfile
|
155
152
|
- Gemfile.lock
|
156
153
|
- LICENSE.txt
|
@@ -185,34 +182,34 @@ files:
|
|
185
182
|
- spec/lib/synapse/service_watcher_ec2tags_spec.rb
|
186
183
|
- spec/lib/synapse/service_watcher_marathon_spec.rb
|
187
184
|
- spec/lib/synapse/service_watcher_spec.rb
|
185
|
+
- spec/lib/synapse/service_watcher_zookeeper_spec.rb
|
188
186
|
- spec/spec_helper.rb
|
189
187
|
- spec/support/configuration.rb
|
190
188
|
- spec/support/minimum.conf.yaml
|
191
189
|
- synapse.gemspec
|
192
190
|
homepage: ''
|
193
191
|
licenses: []
|
192
|
+
metadata: {}
|
194
193
|
post_install_message:
|
195
194
|
rdoc_options: []
|
196
195
|
require_paths:
|
197
196
|
- lib
|
198
197
|
required_ruby_version: !ruby/object:Gem::Requirement
|
199
|
-
none: false
|
200
198
|
requirements:
|
201
|
-
- -
|
199
|
+
- - ">="
|
202
200
|
- !ruby/object:Gem::Version
|
203
201
|
version: '0'
|
204
202
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
|
-
none: false
|
206
203
|
requirements:
|
207
|
-
- -
|
204
|
+
- - ">="
|
208
205
|
- !ruby/object:Gem::Version
|
209
206
|
version: '0'
|
210
207
|
requirements: []
|
211
208
|
rubyforge_project:
|
212
|
-
rubygems_version:
|
209
|
+
rubygems_version: 2.5.1
|
213
210
|
signing_key:
|
214
|
-
specification_version:
|
215
|
-
summary:
|
211
|
+
specification_version: 4
|
212
|
+
summary: ": Write a gem summary"
|
216
213
|
test_files:
|
217
214
|
- spec/lib/synapse/file_output_spec.rb
|
218
215
|
- spec/lib/synapse/haproxy_spec.rb
|
@@ -221,6 +218,7 @@ test_files:
|
|
221
218
|
- spec/lib/synapse/service_watcher_ec2tags_spec.rb
|
222
219
|
- spec/lib/synapse/service_watcher_marathon_spec.rb
|
223
220
|
- spec/lib/synapse/service_watcher_spec.rb
|
221
|
+
- spec/lib/synapse/service_watcher_zookeeper_spec.rb
|
224
222
|
- spec/spec_helper.rb
|
225
223
|
- spec/support/configuration.rb
|
226
224
|
- spec/support/minimum.conf.yaml
|