democracyworks-synapse 0.9.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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.mailmap +3 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +23 -0
- data/Makefile +6 -0
- data/README.md +241 -0
- data/Rakefile +1 -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/lib/synapse.rb +79 -0
- data/lib/synapse/base.rb +5 -0
- data/lib/synapse/haproxy.rb +759 -0
- data/lib/synapse/log.rb +24 -0
- data/lib/synapse/service_watcher.rb +32 -0
- data/lib/synapse/service_watcher/base.rb +105 -0
- data/lib/synapse/service_watcher/dns.rb +103 -0
- data/lib/synapse/service_watcher/docker.rb +115 -0
- data/lib/synapse/service_watcher/ec2tag.rb +26 -0
- data/lib/synapse/service_watcher/zookeeper.rb +127 -0
- data/lib/synapse/version.rb +3 -0
- data/spec/lib/synapse/haproxy_spec.rb +13 -0
- data/spec/lib/synapse/service_watcher_base_spec.rb +55 -0
- data/spec/lib/synapse/service_watcher_docker_spec.rb +138 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/config.rb +9 -0
- data/spec/support/minimum.conf.yaml +27 -0
- data/synapse.gemspec +25 -0
- metadata +155 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MockWatcher; end;
|
4
|
+
|
5
|
+
describe Synapse::Haproxy do
|
6
|
+
subject { Synapse::Haproxy.new(config['haproxy']) }
|
7
|
+
|
8
|
+
it 'updating the config' do
|
9
|
+
mockWatcher = double(Synapse::ServiceWatcher)
|
10
|
+
subject.should_receive(:generate_config)
|
11
|
+
subject.update_config([mockWatcher])
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Synapse::BaseWatcher
|
4
|
+
attr_reader :should_exit, :default_servers
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Synapse::BaseWatcher do
|
8
|
+
let(:mocksynapse) { double() }
|
9
|
+
subject { Synapse::BaseWatcher.new(args, mocksynapse) }
|
10
|
+
let(:testargs) { { 'name' => 'foo', 'discovery' => { 'method' => 'base' }, 'haproxy' => {} }}
|
11
|
+
|
12
|
+
def remove_arg(name)
|
13
|
+
args = testargs.clone
|
14
|
+
args.delete name
|
15
|
+
args
|
16
|
+
end
|
17
|
+
|
18
|
+
context "can construct normally" do
|
19
|
+
let(:args) { testargs }
|
20
|
+
it('can at least construct') { expect { subject }.not_to raise_error }
|
21
|
+
end
|
22
|
+
|
23
|
+
['name', 'discovery', 'haproxy'].each do |to_remove|
|
24
|
+
context "without #{to_remove} argument" do
|
25
|
+
let(:args) { remove_arg to_remove }
|
26
|
+
it('gots bang') { expect { subject }.to raise_error(ArgumentError, "missing required option #{to_remove}") }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "normal tests" do
|
31
|
+
let(:args) { testargs }
|
32
|
+
it('is running') { expect(subject.should_exit).to equal(false) }
|
33
|
+
it('can ping') { expect(subject.ping?).to equal(true) }
|
34
|
+
it('can be stopped') do
|
35
|
+
subject.stop
|
36
|
+
expect(subject.should_exit).to equal(true)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with default_servers" do
|
41
|
+
default_servers = ['server1', 'server2']
|
42
|
+
let(:args) { testargs.merge({'default_servers' => default_servers}) }
|
43
|
+
it('sets default backends to default_servers') { expect(subject.backends).to equal(default_servers) }
|
44
|
+
|
45
|
+
context "with keep_default_servers set" do
|
46
|
+
let(:args) { testargs.merge({'default_servers' => default_servers, 'keep_default_servers' => true}) }
|
47
|
+
let(:new_backends) { ['discovered1', 'discovered2'] }
|
48
|
+
|
49
|
+
it('keeps default_servers when setting backends') do
|
50
|
+
subject.send(:set_backends, new_backends)
|
51
|
+
expect(subject.backends).to eq(default_servers + new_backends)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Synapse::DockerWatcher
|
4
|
+
attr_reader :check_interval, :watcher, :synapse
|
5
|
+
attr_accessor :default_servers
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Synapse::DockerWatcher do
|
9
|
+
let(:mocksynapse) { double() }
|
10
|
+
subject { Synapse::DockerWatcher.new(testargs, mocksynapse) }
|
11
|
+
let(:testargs) { { 'name' => 'foo', 'discovery' => { 'method' => 'docker', 'servers' => [{'host' => 'server1.local', 'name' => 'mainserver'}], 'image_name' => 'mycool/image', 'container_port' => 6379 }, 'haproxy' => {} }}
|
12
|
+
before(:each) do
|
13
|
+
allow(subject.log).to receive(:warn)
|
14
|
+
allow(subject.log).to receive(:info)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_arg(name, value)
|
18
|
+
args = testargs.clone
|
19
|
+
args['discovery'][name] = value
|
20
|
+
args
|
21
|
+
end
|
22
|
+
|
23
|
+
context "can construct normally" do
|
24
|
+
it('can at least construct') { expect { subject }.not_to raise_error }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "normal tests" do
|
28
|
+
it('starts a watcher thread') do
|
29
|
+
watcher_mock = double()
|
30
|
+
expect(Thread).to receive(:new).and_return(watcher_mock)
|
31
|
+
subject.start
|
32
|
+
expect(subject.watcher).to equal(watcher_mock)
|
33
|
+
end
|
34
|
+
it('sets default check interval') do
|
35
|
+
expect(Thread).to receive(:new).and_return(double)
|
36
|
+
subject.start
|
37
|
+
expect(subject.check_interval).to eq(15.0)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "watch tests" do
|
42
|
+
before(:each) do
|
43
|
+
expect(subject).to receive(:sleep_until_next_check) do |arg|
|
44
|
+
subject.instance_variable_set('@should_exit', true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
it('has a happy first run path, configuring backends') do
|
48
|
+
expect(subject).to receive(:containers).and_return(['container1'])
|
49
|
+
expect(subject).to receive(:configure_backends).with(['container1'])
|
50
|
+
subject.send(:watch)
|
51
|
+
end
|
52
|
+
it('does not call configure_backends if there is no change') do
|
53
|
+
expect(subject).to receive(:containers).and_return([])
|
54
|
+
expect(subject).to_not receive(:configure_backends)
|
55
|
+
subject.send(:watch)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
context "watch eats exceptions" do
|
59
|
+
it "blows up when finding containers" do
|
60
|
+
expect(subject).to receive(:containers) do |arg|
|
61
|
+
subject.instance_variable_set('@should_exit', true)
|
62
|
+
raise('throw exception inside watch')
|
63
|
+
end
|
64
|
+
expect { subject.send(:watch) }.not_to raise_error
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "configure_backends tests" do
|
69
|
+
before(:each) do
|
70
|
+
expect(subject.synapse).to receive(:'reconfigure!').at_least(:once)
|
71
|
+
end
|
72
|
+
it 'runs' do
|
73
|
+
expect { subject.send(:configure_backends, []) }.not_to raise_error
|
74
|
+
end
|
75
|
+
it 'sets backends right' do
|
76
|
+
subject.send(:configure_backends, ['foo'])
|
77
|
+
expect(subject.backends).to eq(['foo'])
|
78
|
+
end
|
79
|
+
it 'resets to default backends if no container found' do
|
80
|
+
subject.default_servers = ['fallback1']
|
81
|
+
subject.send(:configure_backends, ['foo'])
|
82
|
+
expect(subject.backends).to eq(['foo'])
|
83
|
+
subject.send(:configure_backends, [])
|
84
|
+
expect(subject.backends).to eq(['fallback1'])
|
85
|
+
end
|
86
|
+
it 'does not reset to default backends if there are no default backends' do
|
87
|
+
subject.default_servers = []
|
88
|
+
subject.send(:configure_backends, ['foo'])
|
89
|
+
expect(subject.backends).to eq(['foo'])
|
90
|
+
subject.send(:configure_backends, [])
|
91
|
+
expect(subject.backends).to eq(['foo'])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "container discovery tests" do
|
96
|
+
before(:each) do
|
97
|
+
getter = double()
|
98
|
+
expect(getter).to receive(:get)
|
99
|
+
expect(Docker).to receive(:connection).and_return(getter)
|
100
|
+
end
|
101
|
+
|
102
|
+
it('has a sane uri') { subject.send(:containers); expect(Docker.url).to eql('http://server1.local:4243') }
|
103
|
+
|
104
|
+
context 'old style port mappings' do
|
105
|
+
context 'works for one container' do
|
106
|
+
let(:docker_data) { [{"Ports" => "0.0.0.0:49153->6379/tcp, 0.0.0.0:49154->6390/tcp", "Image" => "mycool/image:tagname"}] }
|
107
|
+
it do
|
108
|
+
expect(Docker::Util).to receive(:parse_json).and_return(docker_data)
|
109
|
+
expect(subject.send(:containers)).to eql([{"name"=>"mainserver", "host"=>"server1.local", "port"=>"49153"}])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
context 'works for multiple containers' do
|
113
|
+
let(:docker_data) { [{"Ports" => "0.0.0.0:49153->6379/tcp, 0.0.0.0:49154->6390/tcp", "Image" => "mycool/image:tagname"}, {"Ports" => "0.0.0.0:49155->6379/tcp", "Image" => "mycool/image:tagname"}] }
|
114
|
+
it do
|
115
|
+
expect(Docker::Util).to receive(:parse_json).and_return(docker_data)
|
116
|
+
expect(subject.send(:containers)).to eql([{"name"=>"mainserver", "host"=>"server1.local", "port"=>"49153"},{"name"=>"mainserver", "host"=>"server1.local", "port"=>"49155"}])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'new style port mappings' do
|
122
|
+
let(:docker_data) { [{"Ports" => [{'PrivatePort' => 6379, 'PublicPort' => 49153}, {'PublicPort' => 49154, 'PrivatePort' => 6390}], "Image" => "mycool/image:tagname"}] }
|
123
|
+
it do
|
124
|
+
expect(Docker::Util).to receive(:parse_json).and_return(docker_data)
|
125
|
+
expect(subject.send(:containers)).to eql([{"name"=>"mainserver", "host"=>"server1.local", "port"=>"49153"}])
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'filters out wrong images' do
|
130
|
+
let(:docker_data) { [{"Ports" => "0.0.0.0:49153->6379/tcp, 0.0.0.0:49154->6390/tcp", "Image" => "mycool/image:tagname"}, {"Ports" => "0.0.0.0:49155->6379/tcp", "Image" => "wrong/image:tagname"}] }
|
131
|
+
it do
|
132
|
+
expect(Docker::Util).to receive(:parse_json).and_return(docker_data)
|
133
|
+
expect(subject.send(:containers)).to eql([{"name"=>"mainserver", "host"=>"server1.local", "port"=>"49153"}])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
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"
|
8
|
+
require 'pry'
|
9
|
+
require 'support/config'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
config.filter_run :focus
|
15
|
+
config.include Config
|
16
|
+
|
17
|
+
# Run specs in random order to surface order dependencies. If you find an
|
18
|
+
# order dependency and want to debug it, you can fix the order by providing
|
19
|
+
# the seed, which is printed after each run.
|
20
|
+
# --seed 1234
|
21
|
+
config.order = 'random'
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# list the services to connect
|
2
|
+
services:
|
3
|
+
- name: test
|
4
|
+
local_port: 3210
|
5
|
+
server_options: test_option
|
6
|
+
default_servers:
|
7
|
+
- { name: default1, host: localhost, port: 8080}
|
8
|
+
discovery:
|
9
|
+
method: zookeeper
|
10
|
+
path: /airbnb/service/logging/event_collector
|
11
|
+
hosts:
|
12
|
+
- localhost:2181
|
13
|
+
listen:
|
14
|
+
- test_option
|
15
|
+
|
16
|
+
|
17
|
+
# settings for haproxy including the global config
|
18
|
+
haproxy:
|
19
|
+
reload_command: "sudo service haproxy reload"
|
20
|
+
config_file_path: "/etc/haproxy/haproxy.cfg"
|
21
|
+
do_writes: false
|
22
|
+
do_reloads: false
|
23
|
+
global:
|
24
|
+
- global_test_option
|
25
|
+
|
26
|
+
defaults:
|
27
|
+
- default_test_option
|
data/synapse.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'synapse/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "democracyworks-synapse"
|
8
|
+
gem.version = Synapse::VERSION
|
9
|
+
gem.authors = ["Martin Rhoads", "Chris Shea"]
|
10
|
+
gem.email = ["martin.rhoads@airbnb.com", "chris@turbovote.org"]
|
11
|
+
gem.description = %q{: Write a gem description}
|
12
|
+
gem.summary = %q{: Write a gem summary}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
|
19
|
+
gem.add_runtime_dependency "zk", "~> 1.9.2"
|
20
|
+
gem.add_runtime_dependency "docker-api", "~> 1.7.2"
|
21
|
+
|
22
|
+
gem.add_development_dependency "rspec"
|
23
|
+
gem.add_development_dependency "pry"
|
24
|
+
gem.add_development_dependency "pry-nav"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: democracyworks-synapse
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Rhoads
|
8
|
+
- Chris Shea
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-04-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: zk
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 1.9.2
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 1.9.2
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: docker-api
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.7.2
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.7.2
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: pry
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: pry-nav
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
description: ": Write a gem description"
|
85
|
+
email:
|
86
|
+
- martin.rhoads@airbnb.com
|
87
|
+
- chris@turbovote.org
|
88
|
+
executables:
|
89
|
+
- synapse
|
90
|
+
extensions: []
|
91
|
+
extra_rdoc_files: []
|
92
|
+
files:
|
93
|
+
- ".gitignore"
|
94
|
+
- ".mailmap"
|
95
|
+
- ".rspec"
|
96
|
+
- Gemfile
|
97
|
+
- Gemfile.lock
|
98
|
+
- LICENSE.txt
|
99
|
+
- Makefile
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- bin/synapse
|
103
|
+
- config/hostheader_test.json
|
104
|
+
- config/svcdir_test.json
|
105
|
+
- config/synapse.conf.json
|
106
|
+
- config/synapse_services/service1.json
|
107
|
+
- config/synapse_services/service2.json
|
108
|
+
- lib/synapse.rb
|
109
|
+
- lib/synapse/base.rb
|
110
|
+
- lib/synapse/haproxy.rb
|
111
|
+
- lib/synapse/log.rb
|
112
|
+
- lib/synapse/service_watcher.rb
|
113
|
+
- lib/synapse/service_watcher/base.rb
|
114
|
+
- lib/synapse/service_watcher/dns.rb
|
115
|
+
- lib/synapse/service_watcher/docker.rb
|
116
|
+
- lib/synapse/service_watcher/ec2tag.rb
|
117
|
+
- lib/synapse/service_watcher/zookeeper.rb
|
118
|
+
- lib/synapse/version.rb
|
119
|
+
- spec/lib/synapse/haproxy_spec.rb
|
120
|
+
- spec/lib/synapse/service_watcher_base_spec.rb
|
121
|
+
- spec/lib/synapse/service_watcher_docker_spec.rb
|
122
|
+
- spec/spec_helper.rb
|
123
|
+
- spec/support/config.rb
|
124
|
+
- spec/support/minimum.conf.yaml
|
125
|
+
- synapse.gemspec
|
126
|
+
homepage: ''
|
127
|
+
licenses: []
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.2.0
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: ": Write a gem summary"
|
149
|
+
test_files:
|
150
|
+
- spec/lib/synapse/haproxy_spec.rb
|
151
|
+
- spec/lib/synapse/service_watcher_base_spec.rb
|
152
|
+
- spec/lib/synapse/service_watcher_docker_spec.rb
|
153
|
+
- spec/spec_helper.rb
|
154
|
+
- spec/support/config.rb
|
155
|
+
- spec/support/minimum.conf.yaml
|