synapse 0.12.1 → 0.12.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -0
- data/README.md +64 -55
- data/lib/synapse.rb +6 -6
- data/lib/synapse/file_output.rb +12 -1
- data/lib/synapse/haproxy.rb +14 -4
- data/lib/synapse/service_watcher.rb +11 -19
- data/lib/synapse/service_watcher/README.md +84 -0
- data/lib/synapse/service_watcher/base.rb +3 -3
- data/lib/synapse/service_watcher/dns.rb +1 -1
- data/lib/synapse/service_watcher/docker.rb +4 -3
- data/lib/synapse/service_watcher/ec2tag.rb +13 -9
- data/lib/synapse/service_watcher/marathon.rb +112 -0
- data/lib/synapse/service_watcher/zookeeper.rb +12 -7
- data/lib/synapse/service_watcher/zookeeper_dns.rb +3 -3
- data/lib/synapse/version.rb +1 -1
- data/spec/lib/synapse/file_output_spec.rb +61 -0
- data/spec/lib/synapse/haproxy_spec.rb +14 -1
- data/spec/lib/synapse/service_watcher_base_spec.rb +3 -3
- data/spec/lib/synapse/service_watcher_docker_spec.rb +12 -6
- data/spec/lib/synapse/service_watcher_ec2tags_spec.rb +36 -14
- data/spec/lib/synapse/service_watcher_marathon_spec.rb +191 -0
- data/spec/lib/synapse/service_watcher_spec.rb +102 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/minimum.conf.yaml +6 -1
- data/synapse.gemspec +1 -0
- metadata +26 -2
@@ -1,7 +1,7 @@
|
|
1
1
|
require "synapse/service_watcher/base"
|
2
2
|
require 'docker'
|
3
3
|
|
4
|
-
|
4
|
+
class Synapse::ServiceWatcher
|
5
5
|
class DockerWatcher < BaseWatcher
|
6
6
|
def start
|
7
7
|
@check_interval = @discovery['check_interval'] || 15.0
|
@@ -74,10 +74,11 @@ module Synapse
|
|
74
74
|
cnts.each do |cnt|
|
75
75
|
cnt['Ports'] = rewrite_container_ports cnt['Ports']
|
76
76
|
end
|
77
|
-
# Discover containers that match the image/port we're interested in
|
77
|
+
# Discover containers that match the image/port we're interested in and have the port mapped to the host
|
78
78
|
cnts = cnts.find_all do |cnt|
|
79
79
|
cnt["Image"].rpartition(":").first == @discovery["image_name"] \
|
80
|
-
and cnt["Ports"].has_key?(@discovery["container_port"].to_s())
|
80
|
+
and cnt["Ports"].has_key?(@discovery["container_port"].to_s()) \
|
81
|
+
and cnt["Ports"][@discovery["container_port"].to_s()].length > 0
|
81
82
|
end
|
82
83
|
cnts.map do |cnt|
|
83
84
|
{
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'synapse/service_watcher/base'
|
2
2
|
require 'aws-sdk'
|
3
3
|
|
4
|
-
|
5
|
-
class
|
4
|
+
class Synapse::ServiceWatcher
|
5
|
+
class Ec2tagWatcher < BaseWatcher
|
6
6
|
|
7
7
|
attr_reader :check_interval
|
8
8
|
|
@@ -41,15 +41,18 @@ module Synapse
|
|
41
41
|
"Missing server_port_override for service #{@name} - which port are backends listening on?"
|
42
42
|
end
|
43
43
|
|
44
|
-
unless @haproxy['server_port_override'].match(/^\d+$/)
|
44
|
+
unless @haproxy['server_port_override'].to_s.match(/^\d+$/)
|
45
45
|
raise ArgumentError, "Invalid server_port_override value"
|
46
46
|
end
|
47
47
|
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
# aws region is optional in the SDK, aws will use a default value if not provided
|
49
|
+
unless @discovery['aws_region'] || ENV['AWS_REGION']
|
50
|
+
log.info "aws region is missing, will use default"
|
51
|
+
end
|
52
|
+
# access key id & secret are optional, might be using IAM instance profile for credentials
|
53
|
+
unless ((@discovery['aws_access_key_id'] || ENV['aws_access_key_id']) \
|
54
|
+
&& (@discovery['aws_secret_access_key'] || ENV['aws_secret_access_key'] ))
|
55
|
+
log.info "aws access key id & secret not set in config or env variables for service #{name}, will attempt to use IAM instance profile"
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
@@ -60,10 +63,11 @@ module Synapse
|
|
60
63
|
if set_backends(discover_instances)
|
61
64
|
log.info "synapse: ec2tag watcher backends have changed."
|
62
65
|
end
|
63
|
-
sleep_until_next_check(start)
|
64
66
|
rescue Exception => e
|
65
67
|
log.warn "synapse: error in ec2tag watcher thread: #{e.inspect}"
|
66
68
|
log.warn e.backtrace
|
69
|
+
ensure
|
70
|
+
sleep_until_next_check(start)
|
67
71
|
end
|
68
72
|
end
|
69
73
|
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'synapse/service_watcher/base'
|
2
|
+
require 'json'
|
3
|
+
require 'net/http'
|
4
|
+
require 'resolv'
|
5
|
+
|
6
|
+
class Synapse::ServiceWatcher
|
7
|
+
class MarathonWatcher < BaseWatcher
|
8
|
+
def start
|
9
|
+
@check_interval = @discovery['check_interval'] || 10.0
|
10
|
+
@connection = nil
|
11
|
+
@watcher = Thread.new { sleep splay; watch }
|
12
|
+
end
|
13
|
+
|
14
|
+
def stop
|
15
|
+
@connection.finish
|
16
|
+
rescue
|
17
|
+
# pass
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def validate_discovery_opts
|
23
|
+
required_opts = %w[marathon_api_url application_name]
|
24
|
+
|
25
|
+
required_opts.each do |opt|
|
26
|
+
if @discovery.fetch(opt, '').empty?
|
27
|
+
raise ArgumentError,
|
28
|
+
"a value for services.#{@name}.discovery.#{opt} must be specified"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def attempt_marathon_connection
|
34
|
+
marathon_api_path = @discovery.fetch('marathon_api_path', '/v2/apps/%{app}/tasks')
|
35
|
+
marathon_api_path = marathon_api_path % { app: @discovery['application_name'] }
|
36
|
+
|
37
|
+
@marathon_api = URI.join(@discovery['marathon_api_url'], marathon_api_path)
|
38
|
+
|
39
|
+
begin
|
40
|
+
@connection = Net::HTTP.new(@marathon_api.host, @marathon_api.port)
|
41
|
+
@connection.open_timeout = 5
|
42
|
+
@connection.start
|
43
|
+
rescue => ex
|
44
|
+
@connection = nil
|
45
|
+
log.error "synapse: could not connect to marathon at #{@marathon_api}: #{ex}"
|
46
|
+
|
47
|
+
raise ex
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def watch
|
52
|
+
until @should_exit
|
53
|
+
retry_count = 0
|
54
|
+
start = Time.now
|
55
|
+
|
56
|
+
begin
|
57
|
+
if @connection.nil?
|
58
|
+
attempt_marathon_connection
|
59
|
+
end
|
60
|
+
|
61
|
+
req = Net::HTTP::Get.new(@marathon_api.request_uri)
|
62
|
+
req['Accept'] = 'application/json'
|
63
|
+
response = @connection.request(req)
|
64
|
+
|
65
|
+
tasks = JSON.parse(response.body).fetch('tasks', [])
|
66
|
+
port_index = @discovery['port_index'] || 0
|
67
|
+
backends = tasks.keep_if { |task| task['startedAt'] }.map do |task|
|
68
|
+
{
|
69
|
+
'name' => task['host'],
|
70
|
+
'host' => task['host'],
|
71
|
+
'port' => task['ports'][port_index],
|
72
|
+
}
|
73
|
+
end.sort_by { |task| task['name'] }
|
74
|
+
|
75
|
+
invalid_backends = backends.find_all { |b| b['port'].nil? }
|
76
|
+
if invalid_backends.any?
|
77
|
+
backends = backends - invalid_backends
|
78
|
+
|
79
|
+
invalid_backends.each do |backend|
|
80
|
+
log.error "synapse: port index #{port_index} not found in task's port array!"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
set_backends(backends)
|
85
|
+
rescue EOFError
|
86
|
+
# If the persistent HTTP connection is severed, we can automatically
|
87
|
+
# retry
|
88
|
+
log.info "synapse: marathon HTTP API disappeared, reconnecting..."
|
89
|
+
|
90
|
+
retry if (retry_count += 1) == 1
|
91
|
+
rescue => e
|
92
|
+
log.warn "synapse: error in watcher thread: #{e.inspect}"
|
93
|
+
log.warn e.backtrace.join("\n")
|
94
|
+
@connection = nil
|
95
|
+
ensure
|
96
|
+
elapsed_time = Time.now - start
|
97
|
+
sleep (@check_interval - elapsed_time) if elapsed_time < @check_interval
|
98
|
+
end
|
99
|
+
|
100
|
+
@should_exit = true if only_run_once? # for testability
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def splay
|
105
|
+
Random.rand(@check_interval)
|
106
|
+
end
|
107
|
+
|
108
|
+
def only_run_once?
|
109
|
+
false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -3,7 +3,7 @@ require "synapse/service_watcher/base"
|
|
3
3
|
require 'thread'
|
4
4
|
require 'zk'
|
5
5
|
|
6
|
-
|
6
|
+
class Synapse::ServiceWatcher
|
7
7
|
class ZookeeperWatcher < BaseWatcher
|
8
8
|
NUMBERS_RE = /^\d+$/
|
9
9
|
|
@@ -61,7 +61,8 @@ module Synapse
|
|
61
61
|
node = @zk.get("#{@discovery['path']}/#{id}")
|
62
62
|
|
63
63
|
begin
|
64
|
-
|
64
|
+
# TODO: Do less munging, or refactor out this processing
|
65
|
+
host, port, name, weight, haproxy_server_options = deserialize_service_instance(node.first)
|
65
66
|
rescue StandardError => e
|
66
67
|
log.error "synapse: invalid data in ZK node #{id} at #{@discovery['path']}: #{e}"
|
67
68
|
else
|
@@ -72,7 +73,11 @@ module Synapse
|
|
72
73
|
numeric_id = NUMBERS_RE =~ numeric_id ? numeric_id.to_i : nil
|
73
74
|
|
74
75
|
log.debug "synapse: discovered backend #{name} at #{host}:#{server_port} for service #{@name}"
|
75
|
-
new_backends << {
|
76
|
+
new_backends << {
|
77
|
+
'name' => name, 'host' => host, 'port' => server_port,
|
78
|
+
'id' => numeric_id, 'weight' => weight,
|
79
|
+
'haproxy_server_options' => haproxy_server_options
|
80
|
+
}
|
76
81
|
end
|
77
82
|
end
|
78
83
|
|
@@ -84,13 +89,12 @@ module Synapse
|
|
84
89
|
return if @zk.nil?
|
85
90
|
log.debug "synapse: setting watch at #{@discovery['path']}"
|
86
91
|
|
87
|
-
@watcher.
|
88
|
-
@watcher = @zk.register(@discovery['path'], &watcher_callback)
|
92
|
+
@watcher = @zk.register(@discovery['path'], &watcher_callback) unless @watcher
|
89
93
|
|
90
94
|
# Verify that we actually set up the watcher.
|
91
95
|
unless @zk.exists?(@discovery['path'], :watch => true)
|
92
96
|
log.error "synapse: zookeeper watcher path #{@discovery['path']} does not exist!"
|
93
|
-
|
97
|
+
zk_cleanup
|
94
98
|
end
|
95
99
|
log.debug "synapse: set watch at #{@discovery['path']}"
|
96
100
|
end
|
@@ -175,8 +179,9 @@ module Synapse
|
|
175
179
|
port = decoded['port'] || (raise ValueError, 'instance json data does not have port key')
|
176
180
|
name = decoded['name'] || nil
|
177
181
|
weight = decoded['weight'] || nil
|
182
|
+
haproxy_server_options = decoded['haproxy_server_options'] || nil
|
178
183
|
|
179
|
-
return host, port, name, weight
|
184
|
+
return host, port, name, weight, haproxy_server_options
|
180
185
|
end
|
181
186
|
end
|
182
187
|
end
|
@@ -19,7 +19,7 @@ require 'thread'
|
|
19
19
|
# for messages indicating that new servers are available, the check interval
|
20
20
|
# has passed (triggering a re-resolve), or that the watcher should shut down.
|
21
21
|
# The DNS watcher is responsible for the actual reconfiguring of backends.
|
22
|
-
|
22
|
+
class Synapse::ServiceWatcher
|
23
23
|
class ZookeeperDnsWatcher < BaseWatcher
|
24
24
|
|
25
25
|
# Valid messages that can be passed through the internal message queue
|
@@ -46,7 +46,7 @@ module Synapse
|
|
46
46
|
CHECK_INTERVAL_MESSAGE = CheckInterval.new
|
47
47
|
end
|
48
48
|
|
49
|
-
class Dns < Synapse::DnsWatcher
|
49
|
+
class Dns < Synapse::ServiceWatcher::DnsWatcher
|
50
50
|
|
51
51
|
# Overrides the discovery_servers method on the parent class
|
52
52
|
attr_accessor :discovery_servers
|
@@ -106,7 +106,7 @@ module Synapse
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
class Zookeeper < Synapse::ZookeeperWatcher
|
109
|
+
class Zookeeper < Synapse::ServiceWatcher::ZookeeperWatcher
|
110
110
|
def initialize(opts={}, synapse, message_queue)
|
111
111
|
super(opts, synapse)
|
112
112
|
|
data/lib/synapse/version.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
describe Synapse::FileOutput do
|
5
|
+
subject { Synapse::FileOutput.new(config['file_output']) }
|
6
|
+
|
7
|
+
before(:example) do
|
8
|
+
FileUtils.mkdir_p(config['file_output']['output_directory'])
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:example) do
|
12
|
+
FileUtils.rm_r(config['file_output']['output_directory'])
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:mockwatcher_1) do
|
16
|
+
mockWatcher = double(Synapse::ServiceWatcher)
|
17
|
+
allow(mockWatcher).to receive(:name).and_return('example_service')
|
18
|
+
backends = [{ 'host' => 'somehost', 'port' => 5555}]
|
19
|
+
allow(mockWatcher).to receive(:backends).and_return(backends)
|
20
|
+
mockWatcher
|
21
|
+
end
|
22
|
+
let(:mockwatcher_2) do
|
23
|
+
mockWatcher = double(Synapse::ServiceWatcher)
|
24
|
+
allow(mockWatcher).to receive(:name).and_return('foobar_service')
|
25
|
+
backends = [{ 'host' => 'somehost', 'port' => 1234}]
|
26
|
+
allow(mockWatcher).to receive(:backends).and_return(backends)
|
27
|
+
mockWatcher
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'updates the config' do
|
31
|
+
expect(subject).to receive(:write_backends_to_file)
|
32
|
+
subject.update_config([mockwatcher_1])
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'manages correct files' do
|
36
|
+
subject.update_config([mockwatcher_1, mockwatcher_2])
|
37
|
+
FileUtils.cd(config['file_output']['output_directory']) do
|
38
|
+
expect(Dir.glob('*.json')).to eql(['example_service.json', 'foobar_service.json'])
|
39
|
+
end
|
40
|
+
# Should clean up after itself
|
41
|
+
subject.update_config([mockwatcher_1])
|
42
|
+
FileUtils.cd(config['file_output']['output_directory']) do
|
43
|
+
expect(Dir.glob('*.json')).to eql(['example_service.json'])
|
44
|
+
end
|
45
|
+
# Should clean up after itself
|
46
|
+
subject.update_config([])
|
47
|
+
FileUtils.cd(config['file_output']['output_directory']) do
|
48
|
+
expect(Dir.glob('*.json')).to eql([])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'writes correct content' do
|
53
|
+
subject.update_config([mockwatcher_1])
|
54
|
+
data_path = File.join(config['file_output']['output_directory'],
|
55
|
+
"example_service.json")
|
56
|
+
old_backends = JSON.load(File.read(data_path))
|
57
|
+
expect(old_backends.length).to eql(1)
|
58
|
+
expect(old_backends.first['host']).to eql('somehost')
|
59
|
+
expect(old_backends.first['port']).to eql(5555)
|
60
|
+
end
|
61
|
+
end
|
@@ -8,7 +8,16 @@ describe Synapse::Haproxy do
|
|
8
8
|
let(:mockwatcher) do
|
9
9
|
mockWatcher = double(Synapse::ServiceWatcher)
|
10
10
|
allow(mockWatcher).to receive(:name).and_return('example_service')
|
11
|
-
backends = [{ 'host' => 'somehost', 'port' =>
|
11
|
+
backends = [{ 'host' => 'somehost', 'port' => 5555}]
|
12
|
+
allow(mockWatcher).to receive(:backends).and_return(backends)
|
13
|
+
allow(mockWatcher).to receive(:haproxy).and_return({'server_options' => "check inter 2000 rise 3 fall 2"})
|
14
|
+
mockWatcher
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:mockwatcher_with_server_options) do
|
18
|
+
mockWatcher = double(Synapse::ServiceWatcher)
|
19
|
+
allow(mockWatcher).to receive(:name).and_return('example_service')
|
20
|
+
backends = [{ 'host' => 'somehost', 'port' => 5555, 'haproxy_server_options' => 'backup'}]
|
12
21
|
allow(mockWatcher).to receive(:backends).and_return(backends)
|
13
22
|
allow(mockWatcher).to receive(:haproxy).and_return({'server_options' => "check inter 2000 rise 3 fall 2"})
|
14
23
|
mockWatcher
|
@@ -29,4 +38,8 @@ describe Synapse::Haproxy do
|
|
29
38
|
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"]])
|
30
39
|
end
|
31
40
|
|
41
|
+
it 'respects haproxy_server_options' do
|
42
|
+
mockConfig = []
|
43
|
+
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
|
+
end
|
32
45
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
class Synapse::BaseWatcher
|
3
|
+
class Synapse::ServiceWatcher::BaseWatcher
|
4
4
|
attr_reader :should_exit, :default_servers
|
5
5
|
end
|
6
6
|
|
7
|
-
describe Synapse::BaseWatcher do
|
7
|
+
describe Synapse::ServiceWatcher::BaseWatcher do
|
8
8
|
let(:mocksynapse) { double() }
|
9
|
-
subject { Synapse::BaseWatcher.new(args, mocksynapse) }
|
9
|
+
subject { Synapse::ServiceWatcher::BaseWatcher.new(args, mocksynapse) }
|
10
10
|
let(:testargs) { { 'name' => 'foo', 'discovery' => { 'method' => 'base' }, 'haproxy' => {} }}
|
11
11
|
|
12
12
|
def remove_arg(name)
|
@@ -1,13 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'synapse/service_watcher/docker'
|
2
3
|
|
3
|
-
class Synapse::DockerWatcher
|
4
|
+
class Synapse::ServiceWatcher::DockerWatcher
|
4
5
|
attr_reader :check_interval, :watcher, :synapse
|
5
6
|
attr_accessor :default_servers
|
6
7
|
end
|
7
8
|
|
8
|
-
describe Synapse::DockerWatcher do
|
9
|
+
describe Synapse::ServiceWatcher::DockerWatcher do
|
9
10
|
let(:mocksynapse) { double() }
|
10
|
-
subject { Synapse::DockerWatcher.new(testargs, mocksynapse) }
|
11
|
+
subject { Synapse::ServiceWatcher::DockerWatcher.new(testargs, mocksynapse) }
|
11
12
|
let(:testargs) { { 'name' => 'foo', 'discovery' => { 'method' => 'docker', 'servers' => [{'host' => 'server1.local', 'name' => 'mainserver'}], 'image_name' => 'mycool/image', 'container_port' => 6379 }, 'haproxy' => {} }}
|
12
13
|
before(:each) do
|
13
14
|
allow(subject.log).to receive(:warn)
|
@@ -84,9 +85,9 @@ describe Synapse::DockerWatcher do
|
|
84
85
|
it('has a sane uri') { subject.send(:containers); expect(Docker.url).to eql('http://server1.local:4243') }
|
85
86
|
|
86
87
|
context 'old style port mappings' do
|
88
|
+
let(:docker_data) { [{"Ports" => "0.0.0.0:49153->6379/tcp, 0.0.0.0:49154->6390/tcp", "Image" => "mycool/image:tagname"}] }
|
87
89
|
context 'works for one container' do
|
88
|
-
|
89
|
-
it do
|
90
|
+
it do
|
90
91
|
expect(Docker::Util).to receive(:parse_json).and_return(docker_data)
|
91
92
|
expect(subject.send(:containers)).to eql([{"name"=>"mainserver", "host"=>"server1.local", "port"=>"49153"}])
|
92
93
|
end
|
@@ -106,6 +107,12 @@ describe Synapse::DockerWatcher do
|
|
106
107
|
expect(Docker::Util).to receive(:parse_json).and_return(docker_data)
|
107
108
|
expect(subject.send(:containers)).to eql([{"name"=>"mainserver", "host"=>"server1.local", "port"=>"49153"}])
|
108
109
|
end
|
110
|
+
|
111
|
+
it 'filters out containers with unmapped ports' do
|
112
|
+
test_docker_data = docker_data + [{"Ports" => [{'PrivatePort' => 6379}], "Image" => "mycool/image:unmapped"}]
|
113
|
+
expect(Docker::Util).to receive(:parse_json).and_return(test_docker_data)
|
114
|
+
expect(subject.send(:containers)).to eql([{"name"=>"mainserver", "host"=>"server1.local", "port"=>"49153"}])
|
115
|
+
end
|
109
116
|
end
|
110
117
|
|
111
118
|
context 'filters out wrong images' do
|
@@ -117,4 +124,3 @@ describe Synapse::DockerWatcher do
|
|
117
124
|
end
|
118
125
|
end
|
119
126
|
end
|
120
|
-
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'synapse/service_watcher/ec2tag'
|
2
3
|
require 'logging'
|
3
4
|
|
4
|
-
class Synapse::
|
5
|
+
class Synapse::ServiceWatcher::Ec2tagWatcher
|
5
6
|
attr_reader :synapse
|
6
7
|
attr_accessor :default_servers, :ec2
|
7
8
|
end
|
@@ -28,9 +29,9 @@ class FakeAWSInstance
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
|
-
describe Synapse::
|
32
|
+
describe Synapse::ServiceWatcher::Ec2tagWatcher do
|
32
33
|
let(:mock_synapse) { double }
|
33
|
-
subject { Synapse::
|
34
|
+
subject { Synapse::ServiceWatcher::Ec2tagWatcher.new(basic_config, mock_synapse) }
|
34
35
|
|
35
36
|
let(:basic_config) do
|
36
37
|
{ 'name' => 'ec2tagtest',
|
@@ -86,24 +87,24 @@ describe Synapse::EC2Watcher do
|
|
86
87
|
end
|
87
88
|
|
88
89
|
context 'when missing arguments' do
|
89
|
-
it '
|
90
|
+
it 'does not break if aws_region is missing' do
|
90
91
|
expect {
|
91
|
-
Synapse::
|
92
|
-
}.
|
92
|
+
Synapse::ServiceWatcher::Ec2tagWatcher.new(remove_discovery_arg('aws_region'), mock_synapse)
|
93
|
+
}.not_to raise_error
|
93
94
|
end
|
94
|
-
it '
|
95
|
+
it 'does not break if aws_access_key_id is missing' do
|
95
96
|
expect {
|
96
|
-
Synapse::
|
97
|
-
}.
|
97
|
+
Synapse::ServiceWatcher::Ec2tagWatcher.new(remove_discovery_arg('aws_access_key_id'), mock_synapse)
|
98
|
+
}.not_to raise_error
|
98
99
|
end
|
99
|
-
it '
|
100
|
+
it 'does not break if aws_secret_access_key is missing' do
|
100
101
|
expect {
|
101
|
-
Synapse::
|
102
|
-
}.
|
102
|
+
Synapse::ServiceWatcher::Ec2tagWatcher.new(remove_discovery_arg('aws_secret_access_key'), mock_synapse)
|
103
|
+
}.not_to raise_error
|
103
104
|
end
|
104
105
|
it 'complains if server_port_override is missing' do
|
105
106
|
expect {
|
106
|
-
Synapse::
|
107
|
+
Synapse::ServiceWatcher::Ec2tagWatcher.new(remove_haproxy_arg('server_port_override'), mock_synapse)
|
107
108
|
}.to raise_error(ArgumentError, /Missing server_port_override/)
|
108
109
|
end
|
109
110
|
end
|
@@ -111,7 +112,7 @@ describe Synapse::EC2Watcher do
|
|
111
112
|
context 'invalid data' do
|
112
113
|
it 'complains if the haproxy server_port_override is not a number' do
|
113
114
|
expect {
|
114
|
-
Synapse::
|
115
|
+
Synapse::ServiceWatcher::Ec2tagWatcher.new(munge_haproxy_arg('server_port_override', '80deadbeef'), mock_synapse)
|
115
116
|
}.to raise_error(ArgumentError, /Invalid server_port_override/)
|
116
117
|
end
|
117
118
|
end
|
@@ -121,6 +122,27 @@ describe Synapse::EC2Watcher do
|
|
121
122
|
let(:instance1) { FakeAWSInstance.new }
|
122
123
|
let(:instance2) { FakeAWSInstance.new }
|
123
124
|
|
125
|
+
context 'watch' do
|
126
|
+
|
127
|
+
it 'discovers instances, configures backends, then sleeps' do
|
128
|
+
fake_backends = [1,2,3]
|
129
|
+
expect(subject).to receive(:discover_instances).and_return(fake_backends)
|
130
|
+
expect(subject).to receive(:set_backends).with(fake_backends) { subject.stop }
|
131
|
+
expect(subject).to receive(:sleep_until_next_check)
|
132
|
+
subject.send(:watch)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'sleeps until next check if discover_instances fails' do
|
136
|
+
expect(subject).to receive(:discover_instances) do
|
137
|
+
subject.stop
|
138
|
+
raise "discover failed"
|
139
|
+
end
|
140
|
+
expect(subject).to receive(:sleep_until_next_check)
|
141
|
+
subject.send(:watch)
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
124
146
|
context 'using the AWS API' do
|
125
147
|
let(:ec2_client) { double('AWS::EC2') }
|
126
148
|
let(:instance_collection) { double('AWS::EC2::InstanceCollection') }
|