synapse-nginx 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7a5454a83b1bd3322bf0c9b7f558ee981fb62b95
4
+ data.tar.gz: ab54119636b02cfe38d461a80a00dab1922ca339
5
+ SHA512:
6
+ metadata.gz: 4c5def2cb862ce7c5a833e0fa000db8a359e2536162cb5a66f7d605a06d5abeddc105e0cd4115af726938a1053ddbc12c65b0a6682f4c6c0bd1ff5c2fb0f058f
7
+ data.tar.gz: 3b296dad82cebe5e15f5896162d3cd8365f86af6dce0ea36448d78c919a347d8eaa9fb09280299a0e06b3470aa5ca19dc82a112ee5d0104d2e3d85d49667079c
data/.gitignore ADDED
@@ -0,0 +1,52 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ Gemfile.lock
46
+ .ruby-version
47
+ .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
51
+
52
+ .*sw?
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ cache: bundler
3
+ sudo: false
4
+ rvm:
5
+ - 2.0.0-p648
6
+ - 2.1.10
7
+ - 2.2.5
8
+ - 2.3.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in synapse-nginx.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Joseph Lynch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # synapse-nginx
2
+ A config_generator for the Synapse framework that supports NGINX.
3
+
4
+ ## Installation ##
5
+ To install this plugin for Synapse, gem install it after [`synapse`](https://github.com/airbnb/synapse/)
6
+ in your packaging setup.
7
+
8
+ ```bash
9
+ $ gem install synapse --install-dir /opt/smartstack/synapse --no-ri --no-rdoc
10
+ $ gem install synapse-nginx --install-dir /opt/smartstack/synapse --no-ri --no-rdoc
11
+ ```
12
+
13
+ ## Configuration ##
14
+ The NGINX plugin slots right into the standard [`synapse`](https://github.com/airbnb/synapse/)
15
+ configuration file.
16
+
17
+ ### Top level config ###
18
+ The NGINX plugin requires a section to be added to the top level of your
19
+ Synapse [config](https://github.com/airbnb/synapse#configuration). For example:
20
+
21
+ ```yaml
22
+ haproxy:
23
+ # old config for haproxy
24
+ nginx:
25
+ contexts:
26
+ main: [
27
+ 'worker_processes 1',
28
+ 'pid /tmp/nginx.pid'
29
+ ]
30
+ events: [
31
+ 'worker_connections 1024'
32
+ ]
33
+ do_writes: false
34
+ do_reloads: false
35
+ ```
36
+
37
+ The top level `nginx` section of the config file. If provided, it must have
38
+ a `contexts` top level key which contains a hash with the following two
39
+ contexts defined:
40
+
41
+ * `main`: A list of configuration lines to add to the top level of the nginx
42
+ configuration. Typically this contains things like pid files or worker process countes.
43
+ * `events`: A list of configuration lines to add to the events section of the
44
+ nginx configuration. Typically this contians things like worker_connection counts.
45
+
46
+ The following options may be provided to control how nginx writes config:
47
+ * `do_writes`: whether or not the config file will be written (default to `true`)
48
+ * `config_file_path`: where Synapse will write the nginx config file. Required if `do_writes` is set.
49
+ * `check_command`: the command Synapse will run to ensure the generated NGINX config is valid before reloading.
50
+ Required if `do_writes` is set.
51
+
52
+ You can control reloading with:
53
+ * `do_reloads`: whether or not Synapse will reload nginx automatically (default to `true`)
54
+ * `reload_command`: the command Synapse will run to reload NGINX. Required if `do_reloads` is set.
55
+ * `start_command`: the command Synapse will run to start NGINX the first time. Required if `do_reloads` is set.
56
+ * `restart_interval`: number of seconds to wait between restarts of NGINX (default: 2)
57
+ * `restart_jitter`: percentage, expressed as a float, of jitter to multiply the `restart_interval` by when determining the next
58
+ restart time. Use this to help prevent storms when HAProxy restarts. (default: 0.0)
59
+
60
+ You can also provide:
61
+ * `listen_address`: force NGINX to listen on this address (default: localhost)
62
+
63
+ Note that a non-default `listen_address` can be dangerous.
64
+ If you configure an `address:port` combination that is already in use on the system, nginx will fail to start.
65
+
66
+ ### Service watcher config ###
67
+ Each service watcher may supply custom configuration that tells Synapse how to
68
+ configure nginx for just that service.
69
+
70
+ This section is its own hash, which can contain the following keys:
71
+
72
+ * `disabled`: A boolean value indicating if nginx configuration management
73
+ for just this service instance ought be disabled. For example, if you want
74
+ haproxy output for a particular service but not nginx config. (default: `false`)
75
+ * `mode`: The type of proxy, either `http` or `tcp`. This impacts whether
76
+ nginx generates a stream or an http backend for this service. (default: `http`)
77
+ * `upstream`: A list of configuration lines to add to the `upstream` section of
78
+ nginx. (default: [])
79
+ * `server`: A list of configuration lines to add to the `server` section of
80
+ nginx. (default: [])
81
+ * `listen_address`: force nginx to listen on this address (default is localhost).
82
+ Setting `listen_address` on a per service basis overrides the global `listen_address`
83
+ in the top level `nginx` config hash.
84
+ * `upstream_order`: how servers should be ordered in the `upstream` stanza. 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. (default: `shuffle`, which results in random ordering of upstream servers)
85
+ * `upstream_name`: The name of the generated nginx backend for this service
86
+ (defaults to the service's key in the `services` section)
87
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :test => :spec
7
+ task :default => :spec
@@ -0,0 +1,293 @@
1
+ require 'synapse/config_generator/base'
2
+
3
+ require 'fileutils'
4
+ require 'logger'
5
+
6
+ class Synapse::ConfigGenerator
7
+ class Nginx < BaseGenerator
8
+ include Synapse::Logging
9
+
10
+ NAME = 'nginx'.freeze
11
+
12
+ def initialize(opts)
13
+ %w{main events}.each do |req|
14
+ if !opts.fetch('contexts', {}).has_key?(req)
15
+ raise ArgumentError, "nginx requires a contexts.#{req} section"
16
+ end
17
+ end
18
+
19
+ @opts = opts
20
+ @contexts = opts['contexts']
21
+ @opts['do_writes'] = true unless @opts.key?('do_writes')
22
+ @opts['do_reloads'] = true unless @opts.key?('do_reloads')
23
+
24
+ req_pairs = {
25
+ 'do_writes' => ['config_file_path', 'check_command'],
26
+ 'do_reloads' => ['reload_command', 'start_command'],
27
+ }
28
+
29
+ req_pairs.each do |cond, reqs|
30
+ if opts[cond]
31
+ unless reqs.all? {|req| opts[req]}
32
+ missing = reqs.select {|req| not opts[req]}
33
+ raise ArgumentError, "the `#{missing}` option(s) are required when `#{cond}` is true"
34
+ end
35
+ end
36
+ end
37
+
38
+ # how to restart nginx
39
+ @restart_interval = @opts.fetch('restart_interval', 2).to_i
40
+ @restart_jitter = @opts.fetch('restart_jitter', 0).to_f
41
+ @restart_required = true
42
+ @has_started = false
43
+
44
+ # virtual clock bookkeeping for controlling how often nginx restarts
45
+ @time = 0
46
+ @next_restart = @time
47
+
48
+ # a place to store the parsed nginx config from each watcher
49
+ @watcher_configs = {}
50
+ end
51
+
52
+ def normalize_watcher_provided_config(service_watcher_name, service_watcher_config)
53
+ service_watcher_config = super(service_watcher_name, service_watcher_config)
54
+ defaults = {
55
+ 'mode' => 'http',
56
+ 'upstream' => [],
57
+ 'server' => [],
58
+ 'disabled' => false,
59
+ }
60
+ unless service_watcher_config.include?('port')
61
+ log.warn "synapse: service #{service_watcher_name}: nginx config does not include a port; only upstream sections for the service will be created; you must move traffic there manually using server sections"
62
+ end
63
+ defaults.merge(service_watcher_config)
64
+ end
65
+
66
+ def tick(watchers)
67
+ @time += 1
68
+
69
+ # We potentially have to restart if the restart was rate limited
70
+ # in the original call to update_config
71
+ restart if opts['do_reloads'] && @restart_required
72
+ end
73
+
74
+ def update_config(watchers)
75
+ # generate a new config
76
+ new_config = generate_config(watchers)
77
+
78
+ # if we write config files, lets do that and then possibly restart
79
+ if opts['do_writes']
80
+ @restart_required = write_config(new_config)
81
+ restart if opts['do_reloads'] && @restart_required
82
+ end
83
+ end
84
+
85
+ # generates a new config based on the state of the watchers
86
+ def generate_config(watchers)
87
+ new_config = generate_base_config
88
+
89
+ http, stream = [], []
90
+ watchers.each do |watcher|
91
+ watcher_config = watcher.config_for_generator[name]
92
+ next if watcher_config['disabled']
93
+ # There seems to be no way to have empty TCP listeners ... just
94
+ # don't bind the port at all? ... idk
95
+ next if watcher_config['mode'] == 'tcp' && watcher.backends.empty?
96
+
97
+ section = case watcher_config['mode']
98
+ when 'http'
99
+ http
100
+ when 'tcp'
101
+ stream
102
+ else
103
+ raise ArgumentError, "synapse does not understand #{watcher_config['mode']} as a service mode"
104
+ end
105
+ section << generate_server(watcher).flatten
106
+ section << generate_upstream(watcher).flatten
107
+ end
108
+
109
+ unless http.empty?
110
+ new_config << 'http {'
111
+ new_config.concat(http.flatten)
112
+ new_config << "}\n"
113
+ end
114
+
115
+ unless stream.empty?
116
+ new_config << 'stream {'
117
+ new_config.concat(stream.flatten)
118
+ new_config << "}\n"
119
+ end
120
+
121
+ log.debug "synapse: new nginx config: #{new_config}"
122
+ return new_config.flatten.join("\n")
123
+ end
124
+
125
+ # generates the global and defaults sections of the config file
126
+ def generate_base_config
127
+ base_config = ["# auto-generated by synapse at #{Time.now}\n"]
128
+
129
+ # The "main" context is special and is the top level
130
+ @contexts['main'].each do |option|
131
+ base_config << "#{option};"
132
+ end
133
+ base_config << "\n"
134
+
135
+ # http and streams are generated separately
136
+ @contexts.keys.select{|key| !(["main", "http", "stream"].include?(key))}.each do |context|
137
+ base_config << "#{context} {"
138
+ @contexts[context].each do |option|
139
+ base_config << "\t#{option};"
140
+ end
141
+ base_config << "}\n"
142
+ end
143
+ return base_config
144
+ end
145
+
146
+ def generate_server(watcher)
147
+ watcher_config = watcher.config_for_generator[name]
148
+ unless watcher_config.has_key?('port')
149
+ log.debug "synapse: not generating server stanza for watcher #{watcher.name} because it has no port defined"
150
+ return []
151
+ else
152
+ port = watcher_config['port']
153
+ end
154
+
155
+ listen_address = (
156
+ watcher_config['listen_address'] ||
157
+ opts['listen_address'] ||
158
+ 'localhost'
159
+ )
160
+ upstream_name = watcher_config.fetch('upstream_name', watcher.name)
161
+
162
+ stanza = [
163
+ "\tserver {",
164
+ "\t\tlisten #{listen_address}:#{port};",
165
+ watcher_config['server'].map {|c| "\t\t#{c};"},
166
+ generate_proxy(watcher_config['mode'], upstream_name, watcher.backends.empty?),
167
+ "\t}",
168
+ ]
169
+ end
170
+
171
+ # Nginx has some annoying differences between how upstreams in the
172
+ # http (http) module and the stream (tcp) module address upstreams
173
+ def generate_proxy(mode, upstream_name, empty_upstream)
174
+ upstream_name = "http://#{upstream_name}" if mode == 'http'
175
+
176
+ case mode
177
+ when 'http'
178
+ if empty_upstream
179
+ value = "\t\t\treturn 503;"
180
+ else
181
+ value = "\t\t\tproxy_pass #{upstream_name};"
182
+ end
183
+ stanza = [
184
+ "\t\tlocation / {",
185
+ value,
186
+ "\t\t}"
187
+ ]
188
+ when 'tcp'
189
+ stanza = [
190
+ "\t\tproxy_pass #{upstream_name};",
191
+ ]
192
+ else
193
+ []
194
+ end
195
+ end
196
+
197
+ def generate_upstream(watcher)
198
+ backends = {}
199
+ watcher_config = watcher.config_for_generator[name]
200
+ upstream_name = watcher_config.fetch('upstream_name', watcher.name)
201
+
202
+ watcher.backends.each {|b| backends[construct_name(b)] = b}
203
+
204
+ # nginx doesn't like upstreams with no backends?
205
+ return [] if backends.empty?
206
+
207
+ keys = case watcher_config['upstream_order']
208
+ when 'asc'
209
+ backends.keys.sort
210
+ when 'desc'
211
+ backends.keys.sort.reverse
212
+ when 'no_shuffle'
213
+ backends.keys
214
+ else
215
+ backends.keys.shuffle
216
+ end
217
+
218
+ stanza = [
219
+ "\tupstream #{upstream_name} {",
220
+ watcher_config['upstream'].map {|c| "\t\t#{c};"},
221
+ keys.map {|backend_name|
222
+ backend = backends[backend_name]
223
+ b = "\t\tserver #{backend['host']}:#{backend['port']}"
224
+ b = "#{b} #{watcher_config['server_options']}" if watcher_config['server_options']
225
+ "#{b};"
226
+ },
227
+ "\t}"
228
+ ]
229
+ end
230
+
231
+ # writes the config
232
+ def write_config(new_config)
233
+ begin
234
+ old_config = File.read(opts['config_file_path'])
235
+ rescue Errno::ENOENT => e
236
+ log.info "synapse: could not open nginx config file at #{opts['config_file_path']}"
237
+ old_config = ""
238
+ end
239
+
240
+ if old_config == new_config
241
+ return false
242
+ else
243
+ File.open(opts['config_file_path'],'w') {|f| f.write(new_config)}
244
+ check = `#{opts['check_command']}`.chomp
245
+ unless $?.success?
246
+ log.error "synapse: nginx configuration is invalid according to #{opts['check_command']}!"
247
+ log.error 'synapse: not restarting nginx as a result'
248
+ return false
249
+ end
250
+
251
+ return true
252
+ end
253
+ end
254
+
255
+ # restarts nginx if the time is right
256
+ def restart
257
+ if @time < @next_restart
258
+ log.info "synapse: at time #{@time} waiting until #{@next_restart} to restart"
259
+ return
260
+ end
261
+
262
+ @next_restart = @time + @restart_interval
263
+ @next_restart += rand(@restart_jitter * @restart_interval + 1)
264
+
265
+ # do the actual restart
266
+ unless @has_started
267
+ log.info "synapse: attempting to run #{opts['start_command']} to get nginx started"
268
+ log.info 'synapse: this can fail if nginx is already running'
269
+ res = `#{opts['start_command']}`.chomp
270
+ @has_started = true
271
+ end
272
+
273
+ res = `#{opts['reload_command']}`.chomp
274
+ unless $?.success?
275
+ log.error "failed to reload nginx via #{opts['reload_command']}: #{res}"
276
+ return
277
+ end
278
+ log.info "synapse: restarted nginx"
279
+
280
+ @restart_required = false
281
+ end
282
+
283
+ # used to build unique, consistent nginx names for backends
284
+ def construct_name(backend)
285
+ name = "#{backend['host']}:#{backend['port']}"
286
+ if backend['name'] && !backend['name'].empty?
287
+ name = "#{backend['name']}_#{name}"
288
+ end
289
+
290
+ return name
291
+ end
292
+ end
293
+ end
@@ -0,0 +1,6 @@
1
+ module Synapse
2
+ module Nginx
3
+ # Version information for Synapse::Nginx
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+ require 'synapse/config_generator/nginx'
3
+ require 'synapse/service_watcher'
4
+
5
+ class MockWatcher; end;
6
+
7
+ describe Synapse::ConfigGenerator::Nginx do
8
+ subject { Synapse::ConfigGenerator::Nginx.new(config['nginx']) }
9
+
10
+ let(:mockwatcher) do
11
+ mockWatcher = double(Synapse::ServiceWatcher)
12
+ allow(mockWatcher).to receive(:name).and_return('example_service')
13
+ backends = [{ 'host' => 'somehost', 'port' => 5555}]
14
+ allow(mockWatcher).to receive(:backends).and_return(backends)
15
+ watcher_config = subject.normalize_watcher_provided_config('example_service', {'port' => 2199})
16
+ allow(mockWatcher).to receive(:config_for_generator).and_return({
17
+ 'nginx' => watcher_config
18
+ })
19
+ mockWatcher
20
+ end
21
+
22
+ let(:mockwatcher_disabled) do
23
+ mockWatcher = double(Synapse::ServiceWatcher)
24
+ allow(mockWatcher).to receive(:name).and_return('disabled_watcher')
25
+ backends = [{ 'host' => 'somehost', 'port' => 5555}]
26
+ allow(mockWatcher).to receive(:backends).and_return(backends)
27
+ watcher_config = subject.normalize_watcher_provided_config('disabled_watcher', {'port' => 2199, 'disabled' => true})
28
+ allow(mockWatcher).to receive(:config_for_generator).and_return({
29
+ 'nginx' => watcher_config
30
+ })
31
+ mockWatcher
32
+ end
33
+
34
+ describe 'validates arguments' do
35
+ it 'succeeds on minimal config' do
36
+ expect{Synapse::ConfigGenerator::Nginx.new(config['nginx'])}.not_to raise_error
37
+ end
38
+
39
+ it 'validates req_pairs' do
40
+ req_pairs = {
41
+ 'do_writes' => ['config_file_path', 'check_command'],
42
+ 'do_reloads' => ['reload_command', 'start_command'],
43
+ }
44
+ valid_conf = {
45
+ 'contexts' => {'main' => [], 'events' => []},
46
+ 'do_writes' => false,
47
+ 'do_reloads' => false
48
+ }
49
+
50
+ req_pairs.each do |key, value|
51
+ conf = valid_conf.clone
52
+ conf[key] = true
53
+ expect{Synapse::ConfigGenerator::Nginx.new(conf)}.
54
+ to raise_error(ArgumentError, "the `#{value}` option(s) are required when `#{key}` is true")
55
+ end
56
+ end
57
+
58
+ it 'properly defaults do_writes, do_reloads' do
59
+ conf = {
60
+ 'contexts' => {'main' => [], 'events' => []},
61
+ 'config_file_path' => 'test_file',
62
+ 'reload_command' => 'test_reload',
63
+ 'start_command' => 'test_start',
64
+ 'check_command' => 'test_check'
65
+ }
66
+ expect{Synapse::ConfigGenerator::Nginx.new(conf)}.not_to raise_error
67
+ nginx = Synapse::ConfigGenerator::Nginx.new(conf)
68
+ expect(nginx.instance_variable_get(:@opts)['do_writes']).to eql(true)
69
+ expect(nginx.instance_variable_get(:@opts)['do_reloads']).to eql(true)
70
+ end
71
+
72
+ it 'complains when main or events are not passed at all' do
73
+ conf = {
74
+ 'contexts' => {}
75
+ }
76
+ expect{Synapse::ConfigGenerator::Nginx.new(conf)}.to raise_error(ArgumentError)
77
+ end
78
+ end
79
+
80
+ describe '#name' do
81
+ it 'returns nginx' do
82
+ expect(subject.name).to eq('nginx')
83
+ end
84
+ end
85
+
86
+ describe 'disabled watcher' do
87
+ let(:watchers) { [mockwatcher, mockwatcher_disabled] }
88
+
89
+ it 'does not generate config' do
90
+ expect(subject).to receive(:generate_server).exactly(:once).with(mockwatcher).and_return([])
91
+ expect(subject).to receive(:generate_upstream).exactly(:once).with(mockwatcher).and_return([])
92
+ subject.update_config(watchers)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,27 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require "#{File.dirname(__FILE__)}/../lib/synapse-nginx"
8
+ require 'support/configuration'
9
+
10
+ # general RSpec config
11
+ RSpec.configure do |config|
12
+ config.run_all_when_everything_filtered = true
13
+ config.filter_run :focus
14
+ config.include Configuration
15
+
16
+ # verify every double we can think of
17
+ config.mock_with :rspec do |mocks|
18
+ mocks.verify_doubled_constant_names = true
19
+ mocks.verify_partial_doubles = true
20
+ end
21
+
22
+ # Run specs in random order to surface order dependencies. If you find an
23
+ # order dependency and want to debug it, you can fix the order by providing
24
+ # the seed, which is printed after each run.
25
+ # --seed 1234
26
+ config.order = 'random'
27
+ end
@@ -0,0 +1,7 @@
1
+ require 'yaml'
2
+
3
+ module Configuration
4
+ def config
5
+ @config ||= YAML::load_file(File.join(File.dirname(File.expand_path(__FILE__)), 'minimum.conf.yaml'))
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ # list the services to connect
2
+ services:
3
+ test:
4
+ default_servers:
5
+ - { name: default1, host: localhost, port: 8080}
6
+ discovery:
7
+ method: zookeeper
8
+ path: /airbnb/service/logging/event_collector
9
+ hosts:
10
+ - localhost:2181
11
+ nginx:
12
+ port: 3219
13
+ bind_address: 'localhost'
14
+
15
+ # settings for nginx
16
+ nginx:
17
+ contexts:
18
+ main: [
19
+ 'worker_processes 1',
20
+ 'pid /tmp/nginx.pid'
21
+ ]
22
+ events: [
23
+ 'worker_connections 1024'
24
+ ]
25
+ do_writes: false
26
+ do_reloads: false
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'synapse-nginx'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "synapse-nginx"
8
+ gem.version = Synapse::Nginx::VERSION
9
+ gem.authors = ["Joseph Lynch"]
10
+ gem.email = ["jlynch@yelp.com"]
11
+ gem.description = "Nginx config_generator for Synapse"
12
+ gem.licenses = ['MIT']
13
+ gem.summary = %q{Dynamic NGINX configuration plugin for Synapse}
14
+ gem.homepage = "https://github.com/jolynch/synapse-nginx"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+
20
+ gem.add_runtime_dependency "synapse", "~> 0.14"
21
+
22
+ gem.add_development_dependency "rake", "~> 0"
23
+ gem.add_development_dependency "rspec", "~> 3.1"
24
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: synapse-nginx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Joseph Lynch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: synapse
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.14'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ description: Nginx config_generator for Synapse
56
+ email:
57
+ - jlynch@yelp.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".travis.yml"
64
+ - Gemfile
65
+ - LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - lib/synapse-nginx.rb
69
+ - lib/synapse/config_generator/nginx.rb
70
+ - spec/lib/synapse/nginx_spec.rb
71
+ - spec/spec_helper.rb
72
+ - spec/support/configuration.rb
73
+ - spec/support/minimum.conf.yaml
74
+ - synapse-nginx.gemspec
75
+ homepage: https://github.com/jolynch/synapse-nginx
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.2
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Dynamic NGINX configuration plugin for Synapse
99
+ test_files:
100
+ - spec/lib/synapse/nginx_spec.rb
101
+ - spec/spec_helper.rb
102
+ - spec/support/configuration.rb
103
+ - spec/support/minimum.conf.yaml