toxiproxy 0.1.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 841ffa13a93004733dc2adfa28c71bc641ee5981
4
- data.tar.gz: dbbf44472e95affc62b5143685ce50429924ce9e
3
+ metadata.gz: 3198b51c84d89ef6d0b26c989ee633e4def9c4e9
4
+ data.tar.gz: 65dd1628c34e6e4ba4ff7b387b9f1136ff66cfa1
5
5
  SHA512:
6
- metadata.gz: d27b3bec80952c35dec464d1b9cc3d952651af99e209d397a80695f0bd6573749e10846785866f6bb851bd02e4a7437a88f2d27979b46a2b42e3a616a913c2dc
7
- data.tar.gz: b4171531e74753276229f73163afcb44a41b0e4df88929c4746deabed8d4114c5e12060705034e3221a43d6bdc640d7d1a7018303d9f3a06ce91c5be51fdd759
6
+ metadata.gz: 4c91be80c0853f0ad1a8ce5b504f757a99e2b458c61e0a809da82c6918d8f796e8f51377d6580f100cde96b27b16adbf2f686befc67e2a0167c92a28b5582875
7
+ data.tar.gz: b44f5c8d012507b5b4321c80f3ced96f19c9d33114941c6a7ef924b858ce6596324744d2c805aa494b23d049d7119e213d0928f3c100a2f944b0b38d1bd896b4
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # toxiproxy-ruby
2
2
 
3
+ `toxiproxy-ruby` `1.x` (latest) is compatible with the Toxiproxy `2.x` series.
4
+ `toxiproxy-ruby` `0.x` is compatible with the Toxiproxy `1.x` series.
5
+
3
6
  [Toxiproxy](https://github.com/shopify/toxiproxy) is a proxy to simulate network
4
7
  and system conditions. The Ruby API aims to make it simple to write tests that
5
8
  ensure your application behaves appropriately under harsh conditions. Before you
@@ -29,7 +32,7 @@ For example, to simulate 1000ms latency on a database server you can use the
29
32
  list of all toxics):
30
33
 
31
34
  ```ruby
32
- Toxiproxy[:mysql_master].downstream(:latency, latency: 1000).apply do
35
+ Toxiproxy[:mysql_master].toxic(:latency, latency: 1000).apply do
33
36
  Shop.first # this took at least 1s
34
37
  end
35
38
  ```
@@ -60,7 +63,8 @@ Toxiproxy[:cache].upstream(:latency, latency: 1000).apply do
60
63
  end
61
64
  ```
62
65
 
63
- You can apply many toxics to many connections:
66
+ By default the toxic is applied to the downstream connection, you can be
67
+ explicit and chain them:
64
68
 
65
69
  ```ruby
66
70
  Toxiproxy[/redis/].upstream(:slow_close, delay: 100).downstream(:latency, jitter: 300).apply do
@@ -90,4 +94,9 @@ Toxiproxy.populate([{
90
94
  This will create the proxies passed, or replace the proxies if they already exist in Toxiproxy.
91
95
  It's recommended to do this early as early in boot as possible, see the
92
96
  [Toxiproxy README](https://github.com/shopify/toxiproxy#Usage). If you have many
93
- proxies, we recommend storing the Toxiproxy configs in a configuration file.
97
+ proxies, we recommend storing the Toxiproxy configs in a configuration file and
98
+ deserializing it into `Toxiproxy.populate`.
99
+
100
+ If you're doing this in Rails, you may have to do this in `config/boot.rb` (as
101
+ early in boot as possible) as older versions of `ActiveRecord` establish a
102
+ database connection as soon as it's loaded.
@@ -1,9 +1,9 @@
1
1
  #!/bin/bash -e
2
2
 
3
- VERSION='21a264cc75549c3ae837b606eb6e17ea'
3
+ VERSION='v2.0.0rc2'
4
4
  TOXIPROXY_LOG_DIR=${CIRCLE_ARTIFACTS:-'/tmp'}
5
5
 
6
6
  echo "[start toxiproxy]"
7
- curl --silent http://shopify-vagrant.s3.amazonaws.com/toxiproxy/toxiproxy-$VERSION -o ./bin/toxiproxy
8
- chmod +x ./bin/toxiproxy
9
- nohup bash -c "./bin/toxiproxy > ${TOXIPROXY_LOG_DIR}/toxiproxy.log 2>&1 &"
7
+ curl --silent -L https://github.com/Shopify/toxiproxy/releases/download/$VERSION/toxiproxy-server-linux-amd64 -o ./bin/toxiproxy-server
8
+ chmod +x ./bin/toxiproxy-server
9
+ nohup bash -c "./bin/toxiproxy-server > ${TOXIPROXY_LOG_DIR}/toxiproxy.log 2>&1 &"
data/lib/toxiproxy.rb CHANGED
@@ -3,9 +3,9 @@ require "uri"
3
3
  require "net/http"
4
4
  require "forwardable"
5
5
 
6
- require "toxiproxy/collection"
7
6
  require "toxiproxy/toxic"
8
7
  require "toxiproxy/toxic_collection"
8
+ require "toxiproxy/proxy_collection"
9
9
 
10
10
  class Toxiproxy
11
11
  extend SingleForwardable
@@ -26,16 +26,25 @@ class Toxiproxy
26
26
  @enabled = options[:enabled]
27
27
  end
28
28
 
29
- def_delegators :all, *Collection::METHODS
29
+ def_delegators :all, *ProxyCollection::METHODS
30
30
 
31
31
  # Re-enables all proxies and disables all toxics.
32
32
  def self.reset
33
- request = Net::HTTP::Get.new("/reset")
33
+ request = Net::HTTP::Post.new("/reset")
34
34
  response = http.request(request)
35
35
  assert_response(response)
36
36
  self
37
37
  end
38
38
 
39
+ def self.version
40
+ return false unless running?
41
+
42
+ request = Net::HTTP::Get.new("/version")
43
+ response = http.request(request)
44
+ assert_response(response)
45
+ response.body
46
+ end
47
+
39
48
  # Returns a collection of all currently active Toxiproxies.
40
49
  def self.all
41
50
  request = Net::HTTP::Get.new("/proxies")
@@ -51,7 +60,7 @@ class Toxiproxy
51
60
  })
52
61
  }
53
62
 
54
- Collection.new(proxies)
63
+ ProxyCollection.new(proxies)
55
64
  end
56
65
 
57
66
  # Sets the toxiproxy host to use.
@@ -105,20 +114,22 @@ class Toxiproxy
105
114
  end
106
115
 
107
116
  # Set an upstream toxic.
108
- def upstream(toxic = nil, attrs = {})
109
- return @upstream unless toxic
117
+ def upstream(type = nil, attrs = {})
118
+ return @upstream unless type # also alias for the upstream endpoint
110
119
 
111
120
  collection = ToxicCollection.new([self])
112
- collection.upstream(toxic, attrs)
121
+ collection.upstream(type, attrs)
113
122
  collection
114
123
  end
115
124
 
116
125
  # Set a downstream toxic.
117
- def downstream(toxic, attrs = {})
126
+ def downstream(type, attrs = {})
118
127
  collection = ToxicCollection.new([self])
119
- collection.downstream(toxic, attrs)
128
+ collection.downstream(type, attrs)
120
129
  collection
121
130
  end
131
+ alias_method :toxic, :downstream
132
+ alias_method :toxicate, :downstream
122
133
 
123
134
  # Simulates the endpoint is down, by closing the connection and no
124
135
  # longer accepting connections. This is useful to simulate critical system
@@ -180,26 +191,22 @@ class Toxiproxy
180
191
  self
181
192
  end
182
193
 
183
- # Returns a collection of the current toxics for a direction.
184
- def toxics(direction)
185
- unless VALID_DIRECTIONS.include?(direction.to_sym)
186
- raise InvalidToxic, "Toxic direction must be one of: [#{VALID_DIRECTIONS.join(', ')}], got: #{direction}"
187
- end
188
-
189
- request = Net::HTTP::Get.new("/proxies/#{name}/#{direction}/toxics")
194
+ # Returns an array of the current toxics for a direction.
195
+ def toxics
196
+ request = Net::HTTP::Get.new("/proxies/#{name}/toxics")
190
197
  response = http.request(request)
191
198
  assert_response(response)
192
199
 
193
- toxics = JSON.parse(response.body).map { |name, attrs|
194
- Toxic.new({
195
- name: name,
200
+ JSON.parse(response.body).map { |attrs|
201
+ Toxic.new(
202
+ type: attrs['type'],
203
+ name: attrs['name'],
196
204
  proxy: self,
197
- direction: direction,
198
- attrs: attrs
199
- })
205
+ stream: attrs['stream'],
206
+ toxicity: attrs['toxicity'],
207
+ attributes: attrs['attributes'],
208
+ )
200
209
  }
201
-
202
- toxics
203
210
  end
204
211
 
205
212
  private
@@ -7,7 +7,7 @@ class Toxiproxy
7
7
  # Collection instead of an Array (see MRI). Instead, we delegate methods where
8
8
  # it doesn't matter and only allow the filtering methods that really make
9
9
  # sense on a proxy collection.
10
- class Collection
10
+ class ProxyCollection
11
11
  extend Forwardable
12
12
 
13
13
  DELEGATED_METHODS = [:length, :size, :count, :find, :each, :map]
@@ -45,13 +45,13 @@ class Toxiproxy
45
45
  toxics.downstream(toxic, attrs)
46
46
  toxics
47
47
  end
48
+ alias_method :toxicate, :downstream
49
+ alias_method :toxic, :downstream
48
50
 
49
- # Disables all proxies in the collection.
50
51
  def disable
51
52
  @collection.each(&:disable)
52
53
  end
53
54
 
54
- # Enables all proxies in the collection.
55
55
  def enable
56
56
  @collection.each(&:enable)
57
57
  end
@@ -1,51 +1,48 @@
1
1
  class Toxiproxy
2
2
  class Toxic
3
- attr_reader :name, :proxy, :direction
4
- attr_reader :attrs
5
-
6
- def initialize(options)
7
- @proxy = options[:proxy]
8
- @name = options[:name]
9
- @direction = options[:direction]
10
- @attrs = options[:attrs] || {}
3
+ attr_reader :name, :type, :attributes, :stream, :proxy
4
+ attr_accessor :attributes, :toxicity
5
+
6
+ def initialize(attrs)
7
+ raise "Toxic type is required" unless attrs[:type]
8
+ @type = attrs[:type]
9
+ @stream = attrs[:stream] || 'downstream'
10
+ @name = attrs[:name] || "#{@type}_#{@stream}"
11
+ @proxy = attrs[:proxy]
12
+ @toxicity = attrs[:toxicity] || 1.0
13
+ @attributes = attrs[:attributes] || {}
11
14
  end
12
15
 
13
- def enabled?
14
- attrs['enabled']
15
- end
16
+ def save
17
+ request = Net::HTTP::Post.new("/proxies/#{proxy.name}/toxics")
16
18
 
17
- def enable
18
- attrs['enabled'] = true
19
- save
20
- end
19
+ request.body = as_json
21
20
 
22
- def disable
23
- attrs['enabled'] = false
24
- save
25
- end
21
+ response = Toxiproxy.http.request(request)
22
+ Toxiproxy.assert_response(response)
26
23
 
27
- def [](name)
28
- attrs[name]
29
- end
24
+ json = JSON.parse(response.body)
25
+ @attributes = json['attributes']
26
+ @toxicity = json['toxicity']
30
27
 
31
- def []=(name, value)
32
- attrs[name] = value
28
+ self
33
29
  end
34
30
 
35
- def save
36
- unless VALID_DIRECTIONS.include?(direction.to_sym)
37
- raise InvalidToxic, "Toxic direction must be one of: [#{VALID_DIRECTIONS.join(', ')}], got: #{direction}"
38
- end
39
- request = Net::HTTP::Post.new("/proxies/#{proxy.name}/#{direction}/toxics/#{name}")
40
-
41
- request.body = attrs.to_json
42
-
31
+ def destroy
32
+ request = Net::HTTP::Delete.new("/proxies/#{proxy.name}/toxics/#{name}")
43
33
  response = Toxiproxy.http.request(request)
44
34
  Toxiproxy.assert_response(response)
45
-
46
- @attrs = JSON.parse(response.body)
47
-
48
35
  self
49
36
  end
37
+
38
+ def as_json
39
+ {
40
+ name: name,
41
+ type: type,
42
+ stream: stream,
43
+ toxicity: toxicity,
44
+ attributes: attributes,
45
+ }.to_json
46
+ end
50
47
  end
51
48
  end
@@ -13,34 +13,48 @@ class Toxiproxy
13
13
  end
14
14
 
15
15
  def apply(&block)
16
- @toxics.each(&:enable)
17
- yield
18
- ensure
19
- @toxics.each(&:disable)
16
+ names = toxics.group_by { |t| [t.name, t.proxy.name] }
17
+ dups = names.values.select { |toxics| toxics.length > 1 }
18
+ if !dups.empty?
19
+ raise ArgumentError, "There are two toxics with the name #{dups.first[0]} for proxy #{dups.first[1]}, please override the default name (<type>_<direction>)"
20
+ end
21
+
22
+ begin
23
+ @toxics.each(&:save)
24
+ yield
25
+ ensure
26
+ @toxics.each(&:destroy)
27
+ end
20
28
  end
21
29
 
22
- def upstream(toxic_name, attrs = {})
30
+ def upstream(type, attrs = {})
23
31
  proxies.each do |proxy|
24
32
  toxics << Toxic.new(
25
- name: toxic_name,
33
+ name: attrs.delete('name') || attrs.delete(:name),
34
+ type: type,
26
35
  proxy: proxy,
27
- direction: :upstream,
28
- attrs: attrs
36
+ stream: :upstream,
37
+ toxicity: attrs.delete('toxicitiy') || attrs.delete(:toxicity),
38
+ attributes: attrs
29
39
  )
30
40
  end
31
41
  self
32
42
  end
33
43
 
34
- def downstream(toxic_name, attrs = {})
44
+ def downstream(type, attrs = {})
35
45
  proxies.each do |proxy|
36
46
  toxics << Toxic.new(
37
- name: toxic_name,
47
+ name: attrs.delete('name') || attrs.delete(:name),
48
+ type: type,
38
49
  proxy: proxy,
39
- direction: :downstream,
40
- attrs: attrs
50
+ stream: :downstream,
51
+ toxicity: attrs.delete('toxicitiy') || attrs.delete(:toxicity),
52
+ attributes: attrs
41
53
  )
42
54
  end
43
55
  self
44
56
  end
57
+ alias_method :toxic, :downstream
58
+ alias_method :toxicate, :downstream
45
59
  end
46
60
  end
@@ -1,3 +1,3 @@
1
1
  class Toxiproxy
2
- VERSION = "0.1.4"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -31,27 +31,40 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
31
31
  Toxiproxy.host = Toxiproxy::DEFAULT_URI
32
32
  end
33
33
 
34
- def test_enable_and_disable_proxy
34
+ def test_enable_and_disable_proxy_with_toxic
35
35
  with_tcpserver do |port|
36
36
  proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_rubby_server")
37
37
  listen_addr = proxy.listen
38
38
 
39
- Toxiproxy::Toxic.new(
40
- name: 'latency',
41
- proxy: proxy,
42
- direction: :upstream,
43
- attrs: {'latency' => 123}
44
- ).enable
39
+ Toxiproxy::Toxic.new(type: 'latency', attributes: { latency: 123 }, proxy: proxy).save
45
40
 
46
41
  proxy.disable
47
42
  assert_proxy_unavailable proxy
48
43
  proxy.enable
49
44
  assert_proxy_available proxy
50
45
 
51
- latency = proxy.toxics(:upstream).find { |toxic| toxic.name == 'latency' }
52
- assert_equal 123, latency['latency']
53
- assert latency.enabled?
46
+ latency = proxy.toxics.find { |toxic| toxic.name == 'latency_downstream' }
54
47
 
48
+ assert_equal 123, latency.attributes['latency']
49
+ assert_equal listen_addr, proxy.listen
50
+ end
51
+ end
52
+
53
+ def test_delete_toxic
54
+ with_tcpserver do |port|
55
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_rubby_server")
56
+ listen_addr = proxy.listen
57
+
58
+ latency = Toxiproxy::Toxic.new(type: 'latency', attributes: { latency: 123 }, proxy: proxy).save
59
+
60
+ assert_proxy_available proxy
61
+
62
+ latency = proxy.toxics.find { |toxic| toxic.name == 'latency_downstream' }
63
+ assert_equal 123, latency.attributes['latency']
64
+
65
+ latency.destroy
66
+
67
+ assert proxy.toxics.empty?
55
68
  assert_equal listen_addr, proxy.listen
56
69
  end
57
70
  end
@@ -64,20 +77,12 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
64
77
  proxy.disable
65
78
  assert_proxy_unavailable proxy
66
79
 
67
- Toxiproxy::Toxic.new(
68
- name: 'latency',
69
- proxy: proxy,
70
- direction: :upstream,
71
- attrs: {'latency' => 125}
72
- ).enable
80
+ Toxiproxy::Toxic.new(type: 'latency', attributes: { latency: 123 }, proxy: proxy).save
73
81
 
74
82
  Toxiproxy.reset
75
83
  assert_proxy_available proxy
76
84
 
77
- latency = proxy.toxics(:upstream).find { |toxic| toxic.name == 'latency' }
78
- assert_equal 125, latency['latency']
79
- assert !latency.enabled?
80
-
85
+ assert proxy.toxics.empty?
81
86
  assert_equal listen_addr, proxy.listen
82
87
  end
83
88
  end
@@ -104,7 +109,7 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
104
109
  end
105
110
 
106
111
  def test_proxies_all_returns_proxy_collection
107
- assert_instance_of Toxiproxy::Collection, Toxiproxy.all
112
+ assert_instance_of Toxiproxy::ProxyCollection, Toxiproxy.all
108
113
  end
109
114
 
110
115
  def test_down_on_proxy_collection_disables_entire_collection
@@ -154,7 +159,7 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
154
159
  proxies = Toxiproxy.select { |p| p.upstream == "localhost:#{port}" }
155
160
 
156
161
  assert_equal 1, proxies.size
157
- assert_instance_of Toxiproxy::Collection, proxies
162
+ assert_instance_of Toxiproxy::ProxyCollection, proxies
158
163
  end
159
164
  end
160
165
 
@@ -165,7 +170,7 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
165
170
  proxies = Toxiproxy.grep(/\Atest/)
166
171
 
167
172
  assert_equal 1, proxies.size
168
- assert_instance_of Toxiproxy::Collection, proxies
173
+ assert_instance_of Toxiproxy::ProxyCollection, proxies
169
174
  end
170
175
  end
171
176
 
@@ -176,7 +181,7 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
176
181
  proxies = Toxiproxy[/\Atest/]
177
182
 
178
183
  assert_equal 1, proxies.size
179
- assert_instance_of Toxiproxy::Collection, proxies
184
+ assert_instance_of Toxiproxy::ProxyCollection, proxies
180
185
  end
181
186
  end
182
187
 
@@ -218,6 +223,24 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
218
223
  end
219
224
  end
220
225
 
226
+ def test_toxic_applies_a_downstream_toxic
227
+ with_tcpserver(receive: true) do |port|
228
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
229
+
230
+ proxy.toxic(:latency, latency: 100).apply do
231
+ latency = proxy.toxics.find { |toxic| toxic.name == 'latency_downstream' }
232
+
233
+ assert_equal 100, latency.attributes['latency']
234
+ assert_equal 'downstream', latency.stream
235
+ end
236
+ end
237
+ end
238
+
239
+ def test_toxic_default_name_is_type_and_stream
240
+ toxic = Toxiproxy::Toxic.new(type: "latency", stream: "downstream")
241
+ assert_equal "latency_downstream", toxic.name
242
+ end
243
+
221
244
  def test_apply_prolong_toxics
222
245
  with_tcpserver(receive: true) do |port|
223
246
  proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
@@ -361,6 +384,48 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
361
384
  assert_equal true, Toxiproxy.running?
362
385
  end
363
386
 
387
+ def test_version
388
+ assert_instance_of String, Toxiproxy.version
389
+ end
390
+
391
+ def test_multiple_of_same_toxic_type
392
+ with_tcpserver(receive: true) do |port|
393
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
394
+ proxy.toxic(:latency, latency: 100).toxic(:latency, latency: 100, name: "second_latency_downstream").apply do
395
+ before = Time.now
396
+
397
+ socket = connect_to_proxy(proxy)
398
+ socket.write("omg\n")
399
+ socket.flush
400
+ socket.gets
401
+
402
+ passed = Time.now - before
403
+
404
+ assert_in_delta passed, 0.200, 0.01
405
+ end
406
+ end
407
+ end
408
+
409
+ def test_multiple_of_same_toxic_type_with_same_name
410
+ with_tcpserver(receive: true) do |port|
411
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
412
+
413
+ assert_raises ArgumentError do
414
+ proxy.toxic(:latency, latency: 100).toxic(:latency, latency: 100).apply { }
415
+ end
416
+ end
417
+ end
418
+
419
+ def test_invalid_direction
420
+ with_tcpserver(receive: true) do |port|
421
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_rubby_server")
422
+
423
+ assert_raises Toxiproxy::InvalidToxic do
424
+ Toxiproxy::Toxic.new(type: 'latency', attributes: { latency: 123 }, proxy: proxy, stream: 'lolstream').save
425
+ end
426
+ end
427
+ end
428
+
364
429
  private
365
430
 
366
431
  def assert_proxy_available(proxy)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toxiproxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Eskildsen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-03 00:00:00.000000000 Z
12
+ date: 2016-04-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -67,7 +67,7 @@ files:
67
67
  - bin/start-toxiproxy.sh
68
68
  - circle.yml
69
69
  - lib/toxiproxy.rb
70
- - lib/toxiproxy/collection.rb
70
+ - lib/toxiproxy/proxy_collection.rb
71
71
  - lib/toxiproxy/toxic.rb
72
72
  - lib/toxiproxy/toxic_collection.rb
73
73
  - lib/toxiproxy/version.rb