synapse 0.11.1 → 0.12.1
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.
- data/.gitignore +1 -0
- data/.travis.yml +3 -1
- data/Gemfile.lock +32 -16
- data/README.md +120 -95
- data/lib/synapse.rb +20 -6
- data/lib/synapse/file_output.rb +57 -0
- data/lib/synapse/haproxy.rb +122 -25
- data/lib/synapse/service_watcher/base.rb +40 -2
- data/lib/synapse/service_watcher/dns.rb +1 -15
- data/lib/synapse/service_watcher/docker.rb +1 -25
- data/lib/synapse/service_watcher/ec2tag.rb +91 -9
- data/lib/synapse/service_watcher/zookeeper.rb +53 -24
- data/lib/synapse/version.rb +1 -1
- data/spec/lib/synapse/haproxy_spec.rb +22 -3
- data/spec/lib/synapse/service_watcher_base_spec.rb +67 -9
- data/spec/lib/synapse/service_watcher_docker_spec.rb +1 -33
- data/spec/lib/synapse/service_watcher_ec2tags_spec.rb +187 -0
- data/spec/spec_helper.rb +6 -1
- data/spec/support/configuration.rb +0 -2
- data/synapse.gemspec +3 -2
- metadata +29 -10
@@ -1,13 +1,18 @@
|
|
1
1
|
require "synapse/service_watcher/base"
|
2
2
|
|
3
|
+
require 'thread'
|
3
4
|
require 'zk'
|
4
5
|
|
5
6
|
module Synapse
|
6
7
|
class ZookeeperWatcher < BaseWatcher
|
7
8
|
NUMBERS_RE = /^\d+$/
|
8
9
|
|
10
|
+
@@zk_pool = {}
|
11
|
+
@@zk_pool_count = {}
|
12
|
+
@@zk_pool_lock = Mutex.new
|
13
|
+
|
9
14
|
def start
|
10
|
-
@zk_hosts = @discovery['hosts'].
|
15
|
+
@zk_hosts = @discovery['hosts'].sort.join(',')
|
11
16
|
|
12
17
|
@watcher = nil
|
13
18
|
@zk = nil
|
@@ -22,6 +27,8 @@ module Synapse
|
|
22
27
|
end
|
23
28
|
|
24
29
|
def ping?
|
30
|
+
# @zk being nil implies no session *or* a lost session, do not remove
|
31
|
+
# the check on @zk being truthy
|
25
32
|
@zk && @zk.connected?
|
26
33
|
end
|
27
34
|
|
@@ -45,7 +52,7 @@ module Synapse
|
|
45
52
|
@zk.create(path, ignore: :node_exists)
|
46
53
|
end
|
47
54
|
|
48
|
-
# find the current backends at the discovery path
|
55
|
+
# find the current backends at the discovery path
|
49
56
|
def discover
|
50
57
|
log.info "synapse: discovering backends for service #{@name}"
|
51
58
|
|
@@ -54,7 +61,7 @@ module Synapse
|
|
54
61
|
node = @zk.get("#{@discovery['path']}/#{id}")
|
55
62
|
|
56
63
|
begin
|
57
|
-
host, port, name = deserialize_service_instance(node.first)
|
64
|
+
host, port, name, weight = deserialize_service_instance(node.first)
|
58
65
|
rescue StandardError => e
|
59
66
|
log.error "synapse: invalid data in ZK node #{id} at #{@discovery['path']}: #{e}"
|
60
67
|
else
|
@@ -65,26 +72,17 @@ module Synapse
|
|
65
72
|
numeric_id = NUMBERS_RE =~ numeric_id ? numeric_id.to_i : nil
|
66
73
|
|
67
74
|
log.debug "synapse: discovered backend #{name} at #{host}:#{server_port} for service #{@name}"
|
68
|
-
new_backends << { 'name' => name, 'host' => host, 'port' => server_port, 'id' => numeric_id}
|
75
|
+
new_backends << { 'name' => name, 'host' => host, 'port' => server_port, 'id' => numeric_id, 'weight' => weight }
|
69
76
|
end
|
70
77
|
end
|
71
78
|
|
72
|
-
|
73
|
-
if @default_servers.empty?
|
74
|
-
log.warn "synapse: no backends and no default servers for service #{@name}; using previous backends: #{@backends.inspect}"
|
75
|
-
else
|
76
|
-
log.warn "synapse: no backends for service #{@name}; using default servers: #{@default_servers.inspect}"
|
77
|
-
@backends = @default_servers
|
78
|
-
end
|
79
|
-
else
|
80
|
-
log.info "synapse: discovered #{new_backends.length} backends for service #{@name}"
|
81
|
-
set_backends(new_backends)
|
82
|
-
end
|
79
|
+
set_backends(new_backends)
|
83
80
|
end
|
84
81
|
|
85
82
|
# sets up zookeeper callbacks if the data at the discovery path changes
|
86
83
|
def watch
|
87
84
|
return if @zk.nil?
|
85
|
+
log.debug "synapse: setting watch at #{@discovery['path']}"
|
88
86
|
|
89
87
|
@watcher.unsubscribe unless @watcher.nil?
|
90
88
|
@watcher = @zk.register(@discovery['path'], &watcher_callback)
|
@@ -94,6 +92,7 @@ module Synapse
|
|
94
92
|
log.error "synapse: zookeeper watcher path #{@discovery['path']} does not exist!"
|
95
93
|
raise RuntimeError.new('could not set a ZK watch on a node that should exist')
|
96
94
|
end
|
95
|
+
log.debug "synapse: set watch at #{@discovery['path']}"
|
97
96
|
end
|
98
97
|
|
99
98
|
# handles the event that a watched path has changed in zookeeper
|
@@ -103,26 +102,55 @@ module Synapse
|
|
103
102
|
watch
|
104
103
|
# Rediscover
|
105
104
|
discover
|
106
|
-
# send a message to calling class to reconfigure
|
107
|
-
reconfigure!
|
108
105
|
end
|
109
106
|
end
|
110
107
|
|
111
108
|
def zk_cleanup
|
112
109
|
log.info "synapse: zookeeper watcher cleaning up"
|
113
110
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
111
|
+
begin
|
112
|
+
@watcher.unsubscribe unless @watcher.nil?
|
113
|
+
@watcher = nil
|
114
|
+
ensure
|
115
|
+
@@zk_pool_lock.synchronize {
|
116
|
+
if @@zk_pool.has_key?(@zk_hosts)
|
117
|
+
@@zk_pool_count[@zk_hosts] -= 1
|
118
|
+
# Last thread to use the connection closes it
|
119
|
+
if @@zk_pool_count[@zk_hosts] == 0
|
120
|
+
log.info "synapse: closing zk connection to #{@zk_hosts}"
|
121
|
+
begin
|
122
|
+
@zk.close! unless @zk.nil?
|
123
|
+
ensure
|
124
|
+
@@zk_pool.delete(@zk_hosts)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
@zk = nil
|
129
|
+
}
|
130
|
+
end
|
119
131
|
|
120
132
|
log.info "synapse: zookeeper watcher cleaned up successfully"
|
121
133
|
end
|
122
134
|
|
123
135
|
def zk_connect
|
124
136
|
log.info "synapse: zookeeper watcher connecting to ZK at #{@zk_hosts}"
|
125
|
-
|
137
|
+
|
138
|
+
# Ensure that all Zookeeper watcher re-use a single zookeeper
|
139
|
+
# connection to any given set of zk hosts.
|
140
|
+
@@zk_pool_lock.synchronize {
|
141
|
+
unless @@zk_pool.has_key?(@zk_hosts)
|
142
|
+
log.info "synapse: creating pooled connection to #{@zk_hosts}"
|
143
|
+
@@zk_pool[@zk_hosts] = ZK.new(@zk_hosts, :timeout => 5, :thread => :per_callback)
|
144
|
+
@@zk_pool_count[@zk_hosts] = 1
|
145
|
+
log.info "synapse: successfully created zk connection to #{@zk_hosts}"
|
146
|
+
else
|
147
|
+
@@zk_pool_count[@zk_hosts] += 1
|
148
|
+
log.info "synapse: re-using existing zookeeper connection to #{@zk_hosts}"
|
149
|
+
end
|
150
|
+
}
|
151
|
+
|
152
|
+
@zk = @@zk_pool[@zk_hosts]
|
153
|
+
log.info "synapse: retrieved zk connection to #{@zk_hosts}"
|
126
154
|
|
127
155
|
# handle session expiry -- by cleaning up zk, this will make `ping?`
|
128
156
|
# fail and so synapse will exit
|
@@ -146,8 +174,9 @@ module Synapse
|
|
146
174
|
host = decoded['host'] || (raise ValueError, 'instance json data does not have host key')
|
147
175
|
port = decoded['port'] || (raise ValueError, 'instance json data does not have port key')
|
148
176
|
name = decoded['name'] || nil
|
177
|
+
weight = decoded['weight'] || nil
|
149
178
|
|
150
|
-
return host, port, name
|
179
|
+
return host, port, name, weight
|
151
180
|
end
|
152
181
|
end
|
153
182
|
end
|
data/lib/synapse/version.rb
CHANGED
@@ -5,9 +5,28 @@ class MockWatcher; end;
|
|
5
5
|
describe Synapse::Haproxy do
|
6
6
|
subject { Synapse::Haproxy.new(config['haproxy']) }
|
7
7
|
|
8
|
-
|
8
|
+
let(:mockwatcher) do
|
9
9
|
mockWatcher = double(Synapse::ServiceWatcher)
|
10
|
-
|
11
|
-
|
10
|
+
allow(mockWatcher).to receive(:name).and_return('example_service')
|
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
|
12
15
|
end
|
16
|
+
|
17
|
+
it 'updating the config' do
|
18
|
+
expect(subject).to receive(:generate_config)
|
19
|
+
subject.update_config([mockwatcher])
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'generates backend stanza' do
|
23
|
+
mockConfig = []
|
24
|
+
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"]])
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'generates backend stanza without cookies for tcp mode' do
|
28
|
+
mockConfig = ['mode tcp']
|
29
|
+
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
|
+
end
|
31
|
+
|
13
32
|
end
|
@@ -6,7 +6,7 @@ end
|
|
6
6
|
|
7
7
|
describe Synapse::BaseWatcher do
|
8
8
|
let(:mocksynapse) { double() }
|
9
|
-
subject { Synapse::BaseWatcher.new(args, mocksynapse) }
|
9
|
+
subject { Synapse::BaseWatcher.new(args, mocksynapse) }
|
10
10
|
let(:testargs) { { 'name' => 'foo', 'discovery' => { 'method' => 'base' }, 'haproxy' => {} }}
|
11
11
|
|
12
12
|
def remove_arg(name)
|
@@ -37,18 +37,76 @@ describe Synapse::BaseWatcher do
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
context
|
41
|
-
default_servers = [
|
40
|
+
context 'set_backends test' do
|
41
|
+
default_servers = [
|
42
|
+
{'name' => 'default_server1', 'host' => 'default_server1', 'port' => 123},
|
43
|
+
{'name' => 'default_server2', 'host' => 'default_server2', 'port' => 123}
|
44
|
+
]
|
45
|
+
backends = [
|
46
|
+
{'name' => 'server1', 'host' => 'server1', 'port' => 123},
|
47
|
+
{'name' => 'server2', 'host' => 'server2', 'port' => 123}
|
48
|
+
]
|
42
49
|
let(:args) { testargs.merge({'default_servers' => default_servers}) }
|
43
|
-
it('sets default backends to default_servers') { expect(subject.backends).to equal(default_servers) }
|
44
50
|
|
45
|
-
|
46
|
-
|
47
|
-
|
51
|
+
it 'sets backends' do
|
52
|
+
expect(subject).to receive(:'reconfigure!').exactly(:once)
|
53
|
+
expect(subject.send(:set_backends, backends)).to equal(true)
|
54
|
+
expect(subject.backends).to eq(backends)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'removes duplicate backends' do
|
58
|
+
expect(subject).to receive(:'reconfigure!').exactly(:once)
|
59
|
+
duplicate_backends = backends + backends
|
60
|
+
expect(subject.send(:set_backends, duplicate_backends)).to equal(true)
|
61
|
+
expect(subject.backends).to eq(backends)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'sets backends to default_servers if no backends discovered' do
|
65
|
+
expect(subject).to receive(:'reconfigure!').exactly(:once)
|
66
|
+
expect(subject.send(:set_backends, [])).to equal(true)
|
67
|
+
expect(subject.backends).to eq(default_servers)
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'with no default_servers' do
|
71
|
+
let(:args) { remove_arg 'default_servers' }
|
72
|
+
it 'uses previous backends if no default_servers set' do
|
73
|
+
expect(subject).to receive(:'reconfigure!').exactly(:once)
|
74
|
+
expect(subject.send(:set_backends, backends)).to equal(true)
|
75
|
+
expect(subject.send(:set_backends, [])).to equal(false)
|
76
|
+
expect(subject.backends).to eq(backends)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'with no default_servers set and use_previous_backends disabled' do
|
81
|
+
let(:args) {
|
82
|
+
remove_arg 'default_servers'
|
83
|
+
testargs.merge({'use_previous_backends' => false})
|
84
|
+
}
|
85
|
+
it 'removes all backends if no default_servers set and use_previous_backends disabled' do
|
86
|
+
expect(subject).to receive(:'reconfigure!').exactly(:twice)
|
87
|
+
expect(subject.send(:set_backends, backends)).to equal(true)
|
88
|
+
expect(subject.backends).to eq(backends)
|
89
|
+
expect(subject.send(:set_backends, [])).to equal(true)
|
90
|
+
expect(subject.backends).to eq([])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'calls reconfigure only once for duplicate backends' do
|
95
|
+
expect(subject).to receive(:'reconfigure!').exactly(:once)
|
96
|
+
expect(subject.send(:set_backends, backends)).to equal(true)
|
97
|
+
expect(subject.backends).to eq(backends)
|
98
|
+
expect(subject.send(:set_backends, backends)).to equal(false)
|
99
|
+
expect(subject.backends).to eq(backends)
|
100
|
+
end
|
48
101
|
|
102
|
+
context 'with keep_default_servers set' do
|
103
|
+
let(:args) {
|
104
|
+
testargs.merge({'default_servers' => default_servers, 'keep_default_servers' => true})
|
105
|
+
}
|
49
106
|
it('keeps default_servers when setting backends') do
|
50
|
-
subject.
|
51
|
-
expect(subject.backends).to
|
107
|
+
expect(subject).to receive(:'reconfigure!').exactly(:once)
|
108
|
+
expect(subject.send(:set_backends, backends)).to equal(true)
|
109
|
+
expect(subject.backends).to eq(backends + default_servers)
|
52
110
|
end
|
53
111
|
end
|
54
112
|
end
|
@@ -46,12 +46,7 @@ describe Synapse::DockerWatcher do
|
|
46
46
|
end
|
47
47
|
it('has a happy first run path, configuring backends') do
|
48
48
|
expect(subject).to receive(:containers).and_return(['container1'])
|
49
|
-
expect(subject).to receive(:
|
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)
|
49
|
+
expect(subject).to receive(:set_backends).with(['container1'])
|
55
50
|
subject.send(:watch)
|
56
51
|
end
|
57
52
|
end
|
@@ -65,33 +60,6 @@ describe Synapse::DockerWatcher do
|
|
65
60
|
end
|
66
61
|
end
|
67
62
|
|
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
63
|
context "rewrite_container_ports tests" do
|
96
64
|
it 'doesnt break if Ports => nil' do
|
97
65
|
subject.send(:rewrite_container_ports, nil)
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logging'
|
3
|
+
|
4
|
+
class Synapse::EC2Watcher
|
5
|
+
attr_reader :synapse
|
6
|
+
attr_accessor :default_servers, :ec2
|
7
|
+
end
|
8
|
+
|
9
|
+
class FakeAWSInstance
|
10
|
+
def ip_address
|
11
|
+
@ip_address ||= fake_address
|
12
|
+
end
|
13
|
+
|
14
|
+
def private_ip_address
|
15
|
+
@private_ip_address ||= fake_address
|
16
|
+
end
|
17
|
+
|
18
|
+
def dns_name
|
19
|
+
@dns_name ||= "ec2-#{ip_address.gsub('.', '-')}.eu-test-1.compute.amazonaws.com"
|
20
|
+
end
|
21
|
+
|
22
|
+
def private_dns_name
|
23
|
+
@private_dns_name ||= "ip-#{private_ip_address.gsub('.', '-')}.eu-test-1.compute.internal"
|
24
|
+
end
|
25
|
+
|
26
|
+
def fake_address
|
27
|
+
4.times.map { (0...254).to_a.shuffle.pop.to_s }.join('.')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Synapse::EC2Watcher do
|
32
|
+
let(:mock_synapse) { double }
|
33
|
+
subject { Synapse::EC2Watcher.new(basic_config, mock_synapse) }
|
34
|
+
|
35
|
+
let(:basic_config) do
|
36
|
+
{ 'name' => 'ec2tagtest',
|
37
|
+
'haproxy' => {
|
38
|
+
'port' => '8080',
|
39
|
+
'server_port_override' => '8081'
|
40
|
+
},
|
41
|
+
"discovery" => {
|
42
|
+
"method" => "ec2tag",
|
43
|
+
"tag_name" => "fuNNy_tag_name",
|
44
|
+
"tag_value" => "funkyTagValue",
|
45
|
+
"aws_region" => 'eu-test-1',
|
46
|
+
"aws_access_key_id" => 'ABCDEFGHIJKLMNOPQRSTU',
|
47
|
+
"aws_secret_access_key" => 'verylongfakekeythatireallyneedtogenerate'
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
before(:all) do
|
53
|
+
# Clean up ENV so we don't inherit any actual AWS config.
|
54
|
+
%w[AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION].each { |k| ENV.delete(k) }
|
55
|
+
end
|
56
|
+
|
57
|
+
before(:each) do
|
58
|
+
# https://ruby.awsblog.com/post/Tx2SU6TYJWQQLC3/Stubbing-AWS-Responses
|
59
|
+
# always returns empty results, so data may have to be faked.
|
60
|
+
AWS.stub!
|
61
|
+
end
|
62
|
+
|
63
|
+
def remove_discovery_arg(name)
|
64
|
+
args = basic_config.clone
|
65
|
+
args['discovery'].delete name
|
66
|
+
args
|
67
|
+
end
|
68
|
+
|
69
|
+
def remove_haproxy_arg(name)
|
70
|
+
args = basic_config.clone
|
71
|
+
args['haproxy'].delete name
|
72
|
+
args
|
73
|
+
end
|
74
|
+
|
75
|
+
def munge_haproxy_arg(name, new_value)
|
76
|
+
args = basic_config.clone
|
77
|
+
args['haproxy'][name] = new_value
|
78
|
+
args
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#new' do
|
82
|
+
let(:args) { basic_config }
|
83
|
+
|
84
|
+
it 'instantiates cleanly with basic config' do
|
85
|
+
expect { subject }.not_to raise_error
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when missing arguments' do
|
89
|
+
it 'complains if aws_region is missing' do
|
90
|
+
expect {
|
91
|
+
Synapse::EC2Watcher.new(remove_discovery_arg('aws_region'), mock_synapse)
|
92
|
+
}.to raise_error(ArgumentError, /Missing aws_region/)
|
93
|
+
end
|
94
|
+
it 'complains if aws_access_key_id is missing' do
|
95
|
+
expect {
|
96
|
+
Synapse::EC2Watcher.new(remove_discovery_arg('aws_access_key_id'), mock_synapse)
|
97
|
+
}.to raise_error(ArgumentError, /Missing aws_access_key_id/)
|
98
|
+
end
|
99
|
+
it 'complains if aws_secret_access_key is missing' do
|
100
|
+
expect {
|
101
|
+
Synapse::EC2Watcher.new(remove_discovery_arg('aws_secret_access_key'), mock_synapse)
|
102
|
+
}.to raise_error(ArgumentError, /Missing aws_secret_access_key/)
|
103
|
+
end
|
104
|
+
it 'complains if server_port_override is missing' do
|
105
|
+
expect {
|
106
|
+
Synapse::EC2Watcher.new(remove_haproxy_arg('server_port_override'), mock_synapse)
|
107
|
+
}.to raise_error(ArgumentError, /Missing server_port_override/)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'invalid data' do
|
112
|
+
it 'complains if the haproxy server_port_override is not a number' do
|
113
|
+
expect {
|
114
|
+
Synapse::EC2Watcher.new(munge_haproxy_arg('server_port_override', '80deadbeef'), mock_synapse)
|
115
|
+
}.to raise_error(ArgumentError, /Invalid server_port_override/)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "instance discovery" do
|
121
|
+
let(:instance1) { FakeAWSInstance.new }
|
122
|
+
let(:instance2) { FakeAWSInstance.new }
|
123
|
+
|
124
|
+
context 'using the AWS API' do
|
125
|
+
let(:ec2_client) { double('AWS::EC2') }
|
126
|
+
let(:instance_collection) { double('AWS::EC2::InstanceCollection') }
|
127
|
+
|
128
|
+
before do
|
129
|
+
subject.ec2 = ec2_client
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'fetches instances and filter instances' do
|
133
|
+
# Unfortunately there's quite a bit going on here, but this is
|
134
|
+
# a chained call to get then filter EC2 instances, which is
|
135
|
+
# done remotely; breaking into separate calls would result in
|
136
|
+
# unnecessary data being retrieved.
|
137
|
+
|
138
|
+
expect(subject.ec2).to receive(:instances).and_return(instance_collection)
|
139
|
+
|
140
|
+
expect(instance_collection).to receive(:tagged).with('foo').and_return(instance_collection)
|
141
|
+
expect(instance_collection).to receive(:tagged_values).with('bar').and_return(instance_collection)
|
142
|
+
expect(instance_collection).to receive(:select).and_return(instance_collection)
|
143
|
+
|
144
|
+
subject.send(:instances_with_tags, 'foo', 'bar')
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'returned backend data structure' do
|
149
|
+
before do
|
150
|
+
allow(subject).to receive(:instances_with_tags).and_return([instance1, instance2])
|
151
|
+
end
|
152
|
+
|
153
|
+
let(:backends) { subject.send(:discover_instances) }
|
154
|
+
|
155
|
+
it 'returns an Array of backend name/host/port Hashes' do
|
156
|
+
required_keys = %w[name host port]
|
157
|
+
expect(
|
158
|
+
backends.all?{|b| required_keys.each{|k| b.has_key?(k)}}
|
159
|
+
).to be_truthy
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'sets the backend port to server_port_override for all backends' do
|
163
|
+
backends = subject.send(:discover_instances)
|
164
|
+
expect(
|
165
|
+
backends.all? { |b| b['port'] == basic_config['haproxy']['server_port_override'] }
|
166
|
+
).to be_truthy
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'returned instance fields' do
|
171
|
+
before do
|
172
|
+
allow(subject).to receive(:instances_with_tags).and_return([instance1])
|
173
|
+
end
|
174
|
+
|
175
|
+
let(:backend) { subject.send(:discover_instances).pop }
|
176
|
+
|
177
|
+
it "returns an instance's private IP as the hostname" do
|
178
|
+
expect( backend['host'] ).to eq instance1.private_ip_address
|
179
|
+
end
|
180
|
+
|
181
|
+
it "returns an instance's private hostname as the server name" do
|
182
|
+
expect( backend['name'] ).to eq instance1.private_dns_name
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|