synapse 0.12.1 → 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,191 @@
1
+ require 'spec_helper'
2
+ require 'synapse/service_watcher/marathon'
3
+
4
+ describe Synapse::ServiceWatcher::MarathonWatcher do
5
+ let(:mocksynapse) { double() }
6
+ let(:marathon_host) { '127.0.0.1' }
7
+ let(:marathon_port) { '8080' }
8
+ let(:app_name) { 'foo' }
9
+ let(:check_interval) { 11 }
10
+ let(:marathon_request_uri) { "#{marathon_host}:#{marathon_port}/v2/apps/#{app_name}/tasks" }
11
+ let(:config) do
12
+ {
13
+ 'name' => 'foo',
14
+ 'discovery' => {
15
+ 'method' => 'marathon',
16
+ 'marathon_api_url' => "http://#{marathon_host}:#{marathon_port}",
17
+ 'application_name' => app_name,
18
+ 'check_interval' => check_interval,
19
+ },
20
+ 'haproxy' => {},
21
+ }
22
+ end
23
+ let(:marathon_response) { { 'tasks' => [] } }
24
+
25
+ subject { described_class.new(config, mocksynapse) }
26
+
27
+ before do
28
+ allow(subject.log).to receive(:warn)
29
+ allow(subject.log).to receive(:info)
30
+
31
+ allow(Thread).to receive(:new).and_yield
32
+ allow(subject).to receive(:sleep)
33
+ allow(subject).to receive(:only_run_once?).and_return(true)
34
+ allow(subject).to receive(:splay).and_return(0)
35
+
36
+ stub_request(:get, marathon_request_uri).
37
+ with(:headers => { 'Accept' => 'application/json' }).
38
+ to_return(:body => JSON.generate(marathon_response))
39
+ end
40
+
41
+ context 'with a valid argument hash' do
42
+ it 'instantiates' do
43
+ expect(subject).to be_a(Synapse::ServiceWatcher::MarathonWatcher)
44
+ end
45
+ end
46
+
47
+ describe '#watch' do
48
+ context 'when synapse cannot connect to marathon' do
49
+ before do
50
+ allow(Net::HTTP).to receive(:new).
51
+ with(marathon_host, marathon_port.to_i).
52
+ and_raise(Errno::ECONNREFUSED)
53
+ end
54
+
55
+ it 'does not crash' do
56
+ expect { subject.start }.not_to raise_error
57
+ end
58
+ end
59
+
60
+ it 'requests the proper API endpoint one time' do
61
+ subject.start
62
+ expect(a_request(:get, marathon_request_uri)).to have_been_made.times(1)
63
+ end
64
+
65
+ context 'when the API path (marathon_api_path) is customized' do
66
+ let(:config) do
67
+ super().tap do |c|
68
+ c['discovery']['marathon_api_path'] = '/v3/tasks/%{app}'
69
+ end
70
+ end
71
+
72
+ let(:marathon_request_uri) { "#{marathon_host}:#{marathon_port}/v3/tasks/#{app_name}" }
73
+
74
+ it 'calls the customized path' do
75
+ subject.start
76
+ expect(a_request(:get, marathon_request_uri)).to have_been_made.times(1)
77
+ end
78
+ end
79
+
80
+ context 'with tasks returned from marathon' do
81
+ let(:marathon_response) do
82
+ {
83
+ 'tasks' => [
84
+ {
85
+ 'host' => 'agouti.local',
86
+ 'id' => 'my-app_1-1396592790353',
87
+ 'ports' => [
88
+ 31336,
89
+ 31337
90
+ ],
91
+ 'stagedAt' => '2014-04-04T06:26:30.355Z',
92
+ 'startedAt' => '2014-04-04T06:26:30.860Z',
93
+ 'version' => '2014-04-04T06:26:23.051Z'
94
+ },
95
+ ]
96
+ }
97
+ end
98
+ let(:expected_backend_hash) do
99
+ {
100
+ 'name' => 'agouti.local', 'host' => 'agouti.local', 'port' => 31336
101
+ }
102
+ end
103
+
104
+ it 'adds the task as a backend' do
105
+ expect(subject).to receive(:set_backends).with([expected_backend_hash])
106
+ subject.start
107
+ end
108
+
109
+ context 'with a custom port_index' do
110
+ let(:config) do
111
+ super().tap do |c|
112
+ c['discovery']['port_index'] = 1
113
+ end
114
+ end
115
+
116
+ let(:expected_backend_hash) do
117
+ {
118
+ 'name' => 'agouti.local', 'host' => 'agouti.local', 'port' => 31337
119
+ }
120
+ end
121
+
122
+ it 'adds the task as a backend' do
123
+ expect(subject).to receive(:set_backends).with([expected_backend_hash])
124
+ subject.start
125
+ end
126
+
127
+ context 'when that port_index does not exist' do
128
+ let(:config) do
129
+ super().tap { |c| c['discovery']['port_index'] = 999 }
130
+ end
131
+
132
+ it 'does not include the backend' do
133
+ expect(subject).to receive(:set_backends).with([])
134
+ subject.start
135
+ end
136
+ end
137
+ end
138
+
139
+ context 'with a task that has not started yet' do
140
+ let(:marathon_response) do
141
+ super().tap do |resp|
142
+ resp['tasks'] << {
143
+ 'host' => 'agouti.local',
144
+ 'id' => 'my-app_2-1396592790353',
145
+ 'ports' => [
146
+ 31336,
147
+ 31337
148
+ ],
149
+ 'stagedAt' => '2014-04-04T06:26:30.355Z',
150
+ 'startedAt' => nil,
151
+ 'version' => '2014-04-04T06:26:23.051Z'
152
+ }
153
+ end
154
+ end
155
+
156
+ it 'filters tasks that have no startedAt value' do
157
+ expect(subject).to receive(:set_backends).with([expected_backend_hash])
158
+ subject.start
159
+ end
160
+ end
161
+
162
+ context 'when marathon returns invalid response' do
163
+ let(:marathon_response) { [] }
164
+ it 'does not blow up' do
165
+ expect { subject.start }.to_not raise_error
166
+ end
167
+ end
168
+
169
+ context 'when the job takes a long time for some reason' do
170
+ let(:job_duration) { 10 } # seconds
171
+
172
+ before do
173
+ actual_time = Time.now
174
+ time_offset = -1 * job_duration
175
+ allow(Time).to receive(:now) do
176
+ # on first run, return the right time
177
+ # subsequently, add in our job_duration offset
178
+ actual_time + (time_offset += job_duration)
179
+ end
180
+ allow(subject).to receive(:set_backends)
181
+ end
182
+
183
+ it 'only sleeps for the difference' do
184
+ expect(subject).to receive(:sleep).with(check_interval - job_duration)
185
+ subject.start
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+ require 'synapse/service_watcher'
3
+
4
+ describe Synapse::ServiceWatcher do
5
+ let(:mock_synapse) { double }
6
+ subject { Synapse::ServiceWatcher }
7
+ let(:config) do
8
+ {
9
+ 'haproxy' => {
10
+ 'port' => '8080',
11
+ 'server_port_override' => '8081',
12
+ },
13
+ 'discovery' => {
14
+ 'method' => 'test'
15
+ }
16
+ }
17
+ end
18
+
19
+ def replace_discovery(new_value)
20
+ args = config.clone
21
+ args['discovery'] = new_value
22
+ args
23
+ end
24
+
25
+ context 'bogus arguments' do
26
+ it 'complains if discovery method is bogus' do
27
+ expect {
28
+ subject.create('test', config, mock_synapse)
29
+ }.to raise_error(ArgumentError)
30
+ end
31
+ end
32
+
33
+ context 'service watcher dispatch' do
34
+ let (:zookeeper_config) {{
35
+ 'method' => 'zookeeper',
36
+ 'hosts' => 'localhost:2181',
37
+ 'path' => '/smartstack',
38
+ }}
39
+ let (:dns_config) {{
40
+ 'method' => 'dns',
41
+ 'servers' => ['localhost'],
42
+ }}
43
+ let (:docker_config) {{
44
+ 'method' => 'docker',
45
+ 'servers' => 'localhost',
46
+ 'image_name' => 'servicefoo',
47
+ 'container_port' => 1234,
48
+ }}
49
+ let (:ec2_config) {{
50
+ 'method' => 'ec2tag',
51
+ 'tag_name' => 'footag',
52
+ 'tag_value' => 'barvalue',
53
+ 'aws_access_key_id' => 'bogus',
54
+ 'aws_secret_access_key' => 'morebogus',
55
+ 'aws_region' => 'evenmorebogus',
56
+ }}
57
+ let (:zookeeper_dns_config) {{
58
+ 'method' => 'zookeeper_dns',
59
+ 'hosts' => 'localhost:2181',
60
+ 'path' => '/smartstack',
61
+ }}
62
+ let (:marathon_config) {{
63
+ 'method' => 'marathon',
64
+ 'marathon_api_url' => 'localhost:12345',
65
+ 'application_name' => 'foobar',
66
+ }}
67
+
68
+ it 'creates zookeeper correctly' do
69
+ expect {
70
+ subject.create('test', replace_discovery(zookeeper_config), mock_synapse)
71
+ }.not_to raise_error
72
+ end
73
+ it 'creates dns correctly' do
74
+ expect {
75
+ subject.create('test', replace_discovery(dns_config), mock_synapse)
76
+ }.not_to raise_error
77
+ end
78
+ it 'creates docker correctly' do
79
+ expect {
80
+ subject.create('test', replace_discovery(docker_config), mock_synapse)
81
+ }.not_to raise_error
82
+ end
83
+ it 'creates ec2tag correctly' do
84
+ expect {
85
+ subject.create('test', replace_discovery(ec2_config), mock_synapse)
86
+ }.not_to raise_error
87
+ end
88
+ it 'creates zookeeper_dns correctly' do
89
+ expect {
90
+ subject.create('test', replace_discovery(zookeeper_dns_config), mock_synapse)
91
+ }.not_to raise_error
92
+ end
93
+ it 'creates marathon correctly' do
94
+ expect {
95
+ subject.create('test', replace_discovery(marathon_config), mock_synapse)
96
+ }.not_to raise_error
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+
data/spec/spec_helper.rb CHANGED
@@ -7,6 +7,7 @@
7
7
  require "#{File.dirname(__FILE__)}/../lib/synapse"
8
8
  require 'pry'
9
9
  require 'support/configuration'
10
+ require 'webmock/rspec'
10
11
 
11
12
  RSpec.configure do |config|
12
13
  config.run_all_when_everything_filtered = true
@@ -22,6 +22,11 @@ haproxy:
22
22
  do_reloads: false
23
23
  global:
24
24
  - global_test_option
25
-
25
+
26
26
  defaults:
27
27
  - default_test_option
28
+
29
+
30
+ # settings for the file output generator
31
+ file_output:
32
+ output_directory: "/tmp/synapse_file_output_test"
data/synapse.gemspec CHANGED
@@ -24,4 +24,5 @@ Gem::Specification.new do |gem|
24
24
  gem.add_development_dependency "rspec", "~> 3.1.0"
25
25
  gem.add_development_dependency "pry"
26
26
  gem.add_development_dependency "pry-nav"
27
+ gem.add_development_dependency "webmock"
27
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synapse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.12.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-07-24 00:00:00.000000000 Z
12
+ date: 2015-12-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -123,6 +123,22 @@ dependencies:
123
123
  - - ! '>='
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: webmock
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
126
142
  description: ': Write a gem description'
127
143
  email:
128
144
  - martin.rhoads@airbnb.com
@@ -153,17 +169,22 @@ files:
153
169
  - lib/synapse/haproxy.rb
154
170
  - lib/synapse/log.rb
155
171
  - lib/synapse/service_watcher.rb
172
+ - lib/synapse/service_watcher/README.md
156
173
  - lib/synapse/service_watcher/base.rb
157
174
  - lib/synapse/service_watcher/dns.rb
158
175
  - lib/synapse/service_watcher/docker.rb
159
176
  - lib/synapse/service_watcher/ec2tag.rb
177
+ - lib/synapse/service_watcher/marathon.rb
160
178
  - lib/synapse/service_watcher/zookeeper.rb
161
179
  - lib/synapse/service_watcher/zookeeper_dns.rb
162
180
  - lib/synapse/version.rb
181
+ - spec/lib/synapse/file_output_spec.rb
163
182
  - spec/lib/synapse/haproxy_spec.rb
164
183
  - spec/lib/synapse/service_watcher_base_spec.rb
165
184
  - spec/lib/synapse/service_watcher_docker_spec.rb
166
185
  - spec/lib/synapse/service_watcher_ec2tags_spec.rb
186
+ - spec/lib/synapse/service_watcher_marathon_spec.rb
187
+ - spec/lib/synapse/service_watcher_spec.rb
167
188
  - spec/spec_helper.rb
168
189
  - spec/support/configuration.rb
169
190
  - spec/support/minimum.conf.yaml
@@ -193,10 +214,13 @@ signing_key:
193
214
  specification_version: 3
194
215
  summary: ': Write a gem summary'
195
216
  test_files:
217
+ - spec/lib/synapse/file_output_spec.rb
196
218
  - spec/lib/synapse/haproxy_spec.rb
197
219
  - spec/lib/synapse/service_watcher_base_spec.rb
198
220
  - spec/lib/synapse/service_watcher_docker_spec.rb
199
221
  - spec/lib/synapse/service_watcher_ec2tags_spec.rb
222
+ - spec/lib/synapse/service_watcher_marathon_spec.rb
223
+ - spec/lib/synapse/service_watcher_spec.rb
200
224
  - spec/spec_helper.rb
201
225
  - spec/support/configuration.rb
202
226
  - spec/support/minimum.conf.yaml