synapse 0.12.1 → 0.12.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.
@@ -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