toxiproxy 0.0.2 → 0.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2f83e60344122fe413e11816f5c976e46de7d6e
4
- data.tar.gz: 52de814ecc4b5c472329179daf349599c9a5d15a
3
+ metadata.gz: 75c5cd8a2dd3d9a8f0b932d7f50e0359c77c0fda
4
+ data.tar.gz: a6585a9edf06bc9888943292d8784c8aab61685c
5
5
  SHA512:
6
- metadata.gz: 1ae9818932feabbb5982eb525d7dcfd1a03d20047d0e99d2cbd07e3b62d474285e118a29505802571c8132f595a5c014e0bf9327e9a0a6f8358db2836e37f8cb
7
- data.tar.gz: a58e9df0b31c788e9b927fdf7c399621dce64e4f9dcb83e9670ea87ddcfe230a05eb2f3f2be29cf83e23d56ef2e5675a62f6210d66c1006030712cc34a654d36
6
+ metadata.gz: 11f4d1e556c4f942838e3c7d7484e25ed6defecb77985d4609739f25ec383c51aa07c505f64313fb197cfac96611d8ab97d688f6bb8a09a2169112acdf0e763b
7
+ data.tar.gz: 87df2064d0189804a8e84820304a5a9312c767b1337bf3d7941c9ea929bdbaa25de654188ba8cdf9d0a361e0087c86ce5a096c428ec0f1d4b7d073a019f218c6
checksums.yaml.gz.sig ADDED
Binary file
data/README.md CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  [Toxiproxy](https://github.com/shopify/toxiproxy) is a proxy to simulate network
4
4
  and system conditions. The Ruby API aims to make it simple to write tests that
5
- assure your application behaves appropriate under harsh conditions. Please see
6
- the Toxiproxy project for more details.
5
+ ensure your application behaves appropriately under harsh conditions. Before you
6
+ can use the Ruby library, you need to read the [Usage section of the Toxiproxy
7
+ README](https://githubcom/shopify/toxiproxy#Usage).
7
8
 
8
9
  ```
9
10
  gem install toxiproxy
@@ -11,14 +12,19 @@ gem install toxiproxy
11
12
 
12
13
  Make sure the Toxiproxy server is already running.
13
14
 
15
+ For more information about Toxiproxy and the available toxics, see the [Toxiproxy
16
+ documentation](https://github.com/shopify/toxiproxy)
17
+
14
18
  ## Usage
15
19
 
20
+ The Ruby client communicates with the Toxiproxy daemon via HTTP.
21
+
16
22
  For example, to simulate 1000ms latency on a database server you can use the
17
23
  `latency` toxic with the `latency` argument (see the Toxiproxy project for a
18
24
  list of all toxics):
19
25
 
20
26
  ```ruby
21
- Toxiproxy[:mysql_master].downstream(:latency, latency: 1000) do
27
+ Toxiproxy[:mysql_master].downstream(:latency, latency: 1000).apply do
22
28
  Shop.first # this took at least 1s
23
29
  end
24
30
  ```
@@ -44,7 +50,25 @@ If you want to simulate that your cache server is slow at incoming network
44
50
  upstream:
45
51
 
46
52
  ```ruby
47
- Toxiproxy[:cache].upstream(:latency, latency: 1000) do
53
+ Toxiproxy[:cache].upstream(:latency, latency: 1000).apply do
48
54
  Cache.get(:omg) # will take at least a second
49
55
  end
50
56
  ```
57
+
58
+ You can apply many toxics to many connections:
59
+
60
+ ```ruby
61
+ Toxiproxy[/redis/].upstream(:slow_close, delay: 100).downstream(:latency, jitter: 300).apply do
62
+ # all redises are now slow at responding and closing
63
+ end
64
+
65
+ ## Populate
66
+
67
+ To populate Toxiproxy with the proxies from `config/toxiproxy.json`:
68
+
69
+ ```ruby
70
+ Toxiproxy.populate("./config/toxiproxy.json")
71
+ ```
72
+
73
+ It's recommended to do this early as early in boot as possible, see the
74
+ [Toxiproxy README](https://github.com/shopify/toxiproxy#Usage)..
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash -e
2
2
 
3
- VERSION='fad1365c087d6ad53944c8201a6131cb'
3
+ VERSION='0eef7f44653b07ee0e0f8de1cc64b09a'
4
4
 
5
5
  echo "[start toxiproxy]"
6
6
  curl --silent http://shopify-vagrant.s3.amazonaws.com/toxiproxy/toxiproxy-$VERSION -o ./bin/toxiproxy
@@ -28,12 +28,36 @@ class Toxiproxy
28
28
  #
29
29
  # Would simulate every Redis server being down for the duration of the
30
30
  # block.
31
- def down(*args, &block)
31
+ def down(&block)
32
32
  @collection.inject(block) { |nested, proxy|
33
- -> { proxy.down(*args, &nested) }
33
+ -> { proxy.down(&nested) }
34
34
  }.call
35
35
  end
36
36
 
37
+ # Set an upstream toxic.
38
+ def upstream(toxic, attrs = {})
39
+ toxics = ToxicCollection.new(@collection)
40
+ toxics.upstream(toxic, attrs)
41
+ toxics
42
+ end
43
+
44
+ # Set a downstream toxic.
45
+ def downstream(toxic, attrs = {})
46
+ toxics = ToxicCollection.new(@collection)
47
+ toxics.downstream(toxic, attrs)
48
+ toxics
49
+ end
50
+
51
+ # Disables all proxies in the collection.
52
+ def disable
53
+ @collection.each(&:disable)
54
+ end
55
+
56
+ # Enables all proxies in the collection.
57
+ def enable
58
+ @collection.each(&:enable)
59
+ end
60
+
37
61
  # Destroys all toxiproxy's in the collection
38
62
  def destroy
39
63
  @collection.each(&:destroy)
@@ -11,19 +11,23 @@ class Toxiproxy
11
11
  end
12
12
 
13
13
  def enabled?
14
- attrs[:enabled]
14
+ attrs['enabled']
15
15
  end
16
16
 
17
17
  def enable
18
- attrs[:enabled] = true
18
+ attrs['enabled'] = true
19
19
  save
20
20
  end
21
21
 
22
22
  def disable
23
- attrs[:enabled] = false
23
+ attrs['enabled'] = false
24
24
  save
25
25
  end
26
26
 
27
+ def [](name)
28
+ attrs[name]
29
+ end
30
+
27
31
  def []=(name, value)
28
32
  attrs[name] = value
29
33
  end
@@ -3,12 +3,12 @@ class Toxiproxy
3
3
  extend Forwardable
4
4
 
5
5
  attr_accessor :toxics
6
- attr_reader :proxy
6
+ attr_reader :proxies
7
7
 
8
8
  def_delegators :@toxics, :<<, :find
9
9
 
10
- def initialize(proxy)
11
- @proxy = proxy
10
+ def initialize(proxies)
11
+ @proxies = proxies
12
12
  @toxics = []
13
13
  end
14
14
 
@@ -20,22 +20,26 @@ class Toxiproxy
20
20
  end
21
21
 
22
22
  def upstream(toxic_name, attrs = {})
23
- toxics << Toxic.new(
24
- name: toxic_name,
25
- proxy: proxy,
26
- direction: :upstream,
27
- attrs: attrs
28
- )
23
+ proxies.each do |proxy|
24
+ toxics << Toxic.new(
25
+ name: toxic_name,
26
+ proxy: proxy,
27
+ direction: :upstream,
28
+ attrs: attrs
29
+ )
30
+ end
29
31
  self
30
32
  end
31
33
 
32
34
  def downstream(toxic_name, attrs = {})
33
- toxics << Toxic.new(
34
- name: toxic_name,
35
- proxy: proxy,
36
- direction: :downstream,
37
- attrs: attrs
38
- )
35
+ proxies.each do |proxy|
36
+ toxics << Toxic.new(
37
+ name: toxic_name,
38
+ proxy: proxy,
39
+ direction: :downstream,
40
+ attrs: attrs
41
+ )
42
+ end
39
43
  self
40
44
  end
41
45
  end
@@ -1,3 +1,3 @@
1
1
  class Toxiproxy
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/toxiproxy.rb CHANGED
@@ -14,12 +14,13 @@ class Toxiproxy
14
14
  class ProxyExists < StandardError; end
15
15
  class InvalidToxic < StandardError; end
16
16
 
17
- attr_reader :listen, :name
17
+ attr_reader :listen, :name, :enabled
18
18
 
19
19
  def initialize(options)
20
20
  @upstream = options[:upstream]
21
21
  @listen = options[:listen] || "localhost:0"
22
22
  @name = options[:name]
23
+ @enabled = options[:enabled]
23
24
  end
24
25
 
25
26
  # Forwardable doesn't support delegating class methods, so we resort to
@@ -33,6 +34,14 @@ class Toxiproxy
33
34
  end
34
35
  end
35
36
 
37
+ # Re-enables all proxies and disables all toxics.
38
+ def self.reset
39
+ request = Net::HTTP::Get.new("/reset")
40
+ response = http.request(request)
41
+ assert_response(response)
42
+ self
43
+ end
44
+
36
45
  # Returns a collection of all currently active Toxiproxies.
37
46
  def self.all
38
47
  request = Net::HTTP::Get.new("/proxies")
@@ -43,7 +52,8 @@ class Toxiproxy
43
52
  self.new({
44
53
  upstream: attrs["upstream"],
45
54
  listen: attrs["listen"],
46
- name: attrs["name"]
55
+ name: attrs["name"],
56
+ enabled: attrs["enabled"]
47
57
  })
48
58
  }
49
59
 
@@ -57,7 +67,12 @@ class Toxiproxy
57
67
 
58
68
  # Find a single proxy by name.
59
69
  def self.find_by_name(name = nil, &block)
60
- proxy = self.all.find { |p| p.name == name.to_s }
70
+ self.all.find { |p| p.name == name.to_s }
71
+ end
72
+
73
+ # Calls find_by_name and raises NotFound if not found
74
+ def self.find_by_name!(*args)
75
+ proxy = find_by_name(*args)
61
76
  raise NotFound, "#{name} not found in #{self.all.map(&:name).join(', ')}" unless proxy
62
77
  proxy
63
78
  end
@@ -67,21 +82,30 @@ class Toxiproxy
67
82
  # name.
68
83
  def self.[](query)
69
84
  return grep(query) if query.is_a?(Regexp)
70
- find_by_name(query)
85
+ find_by_name!(query)
86
+ end
87
+
88
+ def self.populate(path)
89
+ proxies = JSON.parse(File.read(path), symbolize_names: true)
90
+ proxies = proxies.map { |proxy| self.new(proxy) }
91
+
92
+ proxies.each do |proxy|
93
+ proxy.create unless find_by_name(proxy.name)
94
+ end
71
95
  end
72
96
 
73
97
  # Set an upstream toxic.
74
98
  def upstream(toxic = nil, attrs = {})
75
99
  return @upstream unless toxic
76
100
 
77
- collection = ToxicCollection.new(self)
101
+ collection = ToxicCollection.new([self])
78
102
  collection.upstream(toxic, attrs)
79
103
  collection
80
104
  end
81
105
 
82
106
  # Set a downstream toxic.
83
107
  def downstream(toxic, attrs = {})
84
- collection = ToxicCollection.new(self)
108
+ collection = ToxicCollection.new([self])
85
109
  collection.downstream(toxic, attrs)
86
110
  collection
87
111
  end
@@ -90,25 +114,45 @@ class Toxiproxy
90
114
  # longer accepting connections. This is useful to simulate critical system
91
115
  # failure, such as a data store becoming completely unavailable.
92
116
  def down(&block)
93
- uptoxics = toxics(:upstream)
94
- downtoxics = toxics(:downstream)
95
- destroy
117
+ disable
96
118
  begin
97
119
  yield
98
120
  ensure
99
- create
100
- uptoxics.each(&:save)
101
- downtoxics.each(&:save)
121
+ enable
102
122
  end
103
123
  end
104
124
 
125
+ # Disables a Toxiproxy. This will drop all active connections and stop the proxy from listening.
126
+ def disable
127
+ request = Net::HTTP::Post.new("/proxies/#{name}")
128
+
129
+ hash = {enabled: false}
130
+ request.body = hash.to_json
131
+
132
+ response = http.request(request)
133
+ assert_response(response)
134
+ self
135
+ end
136
+
137
+ # Enables a Toxiproxy. This will cause the proxy to start listening again.
138
+ def enable
139
+ request = Net::HTTP::Post.new("/proxies/#{name}")
140
+
141
+ hash = {enabled: true}
142
+ request.body = hash.to_json
143
+
144
+ response = http.request(request)
145
+ assert_response(response)
146
+ self
147
+ end
148
+
105
149
  # Create a Toxiproxy, proxying traffic from `@listen` (optional argument to
106
150
  # the constructor) to `@upstream`. `#down` `#upstream` or `#downstream` can at any time alter the health
107
151
  # of this connection.
108
152
  def create
109
153
  request = Net::HTTP::Post.new("/proxies")
110
154
 
111
- hash = {upstream: upstream, name: name, listen: listen}
155
+ hash = {upstream: upstream, name: name, listen: listen, enabled: enabled}
112
156
  request.body = hash.to_json
113
157
 
114
158
  response = http.request(request)
@@ -128,8 +172,6 @@ class Toxiproxy
128
172
  self
129
173
  end
130
174
 
131
- private
132
-
133
175
  # Returns a collection of the current toxics for a direction.
134
176
  def toxics(direction)
135
177
  unless VALID_DIRECTIONS.include?(direction.to_sym)
@@ -152,6 +194,8 @@ class Toxiproxy
152
194
  toxics
153
195
  end
154
196
 
197
+ private
198
+
155
199
  def self.http
156
200
  @http ||= Net::HTTP.new(URI.host, URI.port)
157
201
  end
@@ -0,0 +1,7 @@
1
+ [
2
+ {
3
+ "name": "toxiproxy_test_mysql",
4
+ "upstream": "localhost:3306",
5
+ "listen": "localhost:22222"
6
+ }
7
+ ]
@@ -24,6 +24,57 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
24
24
  assert_equal "test_mysql_master", proxy.name
25
25
  end
26
26
 
27
+ def test_enable_and_disable_proxy
28
+ with_tcpserver do |port|
29
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_rubby_server")
30
+ listen_addr = proxy.listen
31
+
32
+ Toxiproxy::Toxic.new(
33
+ name: 'latency',
34
+ proxy: proxy,
35
+ direction: :upstream,
36
+ attrs: {'latency' => 123}
37
+ ).enable
38
+
39
+ proxy.disable
40
+ assert_proxy_unavailable proxy
41
+ proxy.enable
42
+ assert_proxy_available proxy
43
+
44
+ latency = proxy.toxics(:upstream).find { |toxic| toxic.name == 'latency' }
45
+ assert_equal 123, latency['latency']
46
+ assert latency.enabled?
47
+
48
+ assert_equal listen_addr, proxy.listen
49
+ end
50
+ end
51
+
52
+ def test_reset
53
+ with_tcpserver do |port|
54
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_rubby_server")
55
+ listen_addr = proxy.listen
56
+
57
+ proxy.disable
58
+ assert_proxy_unavailable proxy
59
+
60
+ Toxiproxy::Toxic.new(
61
+ name: 'latency',
62
+ proxy: proxy,
63
+ direction: :upstream,
64
+ attrs: {'latency' => 125}
65
+ ).enable
66
+
67
+ Toxiproxy.reset
68
+ assert_proxy_available proxy
69
+
70
+ latency = proxy.toxics(:upstream).find { |toxic| toxic.name == 'latency' }
71
+ assert_equal 125, latency['latency']
72
+ assert !latency.enabled?
73
+
74
+ assert_equal listen_addr, proxy.listen
75
+ end
76
+ end
77
+
27
78
  def test_take_endpoint_down
28
79
  with_tcpserver do |port|
29
80
  proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_rubby_server")
@@ -69,6 +120,26 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
69
120
  end
70
121
  end
71
122
 
123
+ def test_disable_on_proxy_collection
124
+ with_tcpserver do |port1|
125
+ with_tcpserver do |port2|
126
+ proxy1 = Toxiproxy.create(upstream: "localhost:#{port1}", name: "test_proxy1")
127
+ proxy2 = Toxiproxy.create(upstream: "localhost:#{port2}", name: "test_proxy2")
128
+
129
+ assert_proxy_available proxy2
130
+ assert_proxy_available proxy1
131
+
132
+ Toxiproxy.all.disable
133
+ assert_proxy_unavailable proxy1
134
+ assert_proxy_unavailable proxy2
135
+ Toxiproxy.all.enable
136
+
137
+ assert_proxy_available proxy2
138
+ assert_proxy_available proxy1
139
+ end
140
+ end
141
+ end
142
+
72
143
  def test_select_from_toxiproxy_collection
73
144
  with_tcpserver do |port|
74
145
  Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
@@ -103,8 +174,6 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
103
174
  end
104
175
 
105
176
  def test_apply_upstream_toxic
106
- $before = Time.now
107
-
108
177
  with_tcpserver(receive: true) do |port|
109
178
  proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
110
179
 
@@ -161,6 +230,47 @@ class ToxiproxyTest < MiniTest::Unit::TestCase
161
230
  end
162
231
  end
163
232
 
233
+ def test_apply_toxics_to_collection
234
+ with_tcpserver(receive: true) do |port1|
235
+ with_tcpserver(receive: true) do |port2|
236
+ proxy1 = Toxiproxy.create(upstream: "localhost:#{port1}", name: "test_proxy1")
237
+ proxy2 = Toxiproxy.create(upstream: "localhost:#{port2}", name: "test_proxy2")
238
+
239
+ Toxiproxy[/test_proxy/].upstream(:latency, latency: 100).downstream(:latency, latency: 100).apply do
240
+ before = Time.now
241
+
242
+ socket = connect_to_proxy(proxy1)
243
+ socket.write("omg\n")
244
+ socket.flush
245
+ socket.gets
246
+
247
+ passed = Time.now - before
248
+
249
+ assert_in_delta passed, 0.200, 0.01
250
+
251
+ before = Time.now
252
+
253
+ socket = connect_to_proxy(proxy2)
254
+ socket.write("omg\n")
255
+ socket.flush
256
+ socket.gets
257
+
258
+ passed = Time.now - before
259
+
260
+ assert_in_delta passed, 0.200, 0.01
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ def test_populate_creates_proxies
267
+ proxies = Toxiproxy.populate("./test/fixtures/toxiproxy.json")
268
+
269
+ proxies.each do |proxy|
270
+ assert_proxy_available(proxy)
271
+ end
272
+ end
273
+
164
274
  private
165
275
 
166
276
  def assert_proxy_available(proxy)
data.tar.gz.sig ADDED
Binary file
metadata CHANGED
@@ -1,15 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toxiproxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Eskildsen
8
8
  - Jacob Wirth
9
9
  autorequire:
10
10
  bindir: bin
11
- cert_chain: []
12
- date: 2014-11-07 00:00:00.000000000 Z
11
+ cert_chain:
12
+ - |
13
+ -----BEGIN CERTIFICATE-----
14
+ MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQ8wDQYDVQQDDAZhZG1p
15
+ bnMxFzAVBgoJkiaJk/IsZAEZFgdzaG9waWZ5MRMwEQYKCZImiZPyLGQBGRYDY29t
16
+ MB4XDTE0MDUxNTIwMzM0OFoXDTE1MDUxNTIwMzM0OFowPzEPMA0GA1UEAwwGYWRt
17
+ aW5zMRcwFQYKCZImiZPyLGQBGRYHc2hvcGlmeTETMBEGCgmSJomT8ixkARkWA2Nv
18
+ bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL0/81O3e1vh5smcwp2G
19
+ MpLQ6q0kejQLa65bPYPxdzWA1SYOKyGfw+yR9LdFzsuKpwWzKq6zX35lj1IckWS4
20
+ bNBEQzxmufUxU0XPM02haFB8fOfDJzdXsWte9Ge4IFwahwn68gpMqN+BvxL+KMYz
21
+ Iut9YmN44d4LZdsENEIO5vmybuG2vYDz7R56qB0PA+Q2P2CdhymsBad2DQs69FBo
22
+ uico9V6VMYYctL9lCYdzu9IXrOYNTt88suKIVzzAlHOKeN0Ng5qdztFoTR8sfxDr
23
+ Ydg3KHl5n47wlpgd8R0f/4b5gGxW+v9pyJCgQnLlRu7DedVSvv7+GMtj3g9r3nhJ
24
+ KqECAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFI/o
25
+ maf34HXbUOQsdoLHacEKQgunMB0GA1UdEQQWMBSBEmFkbWluc0BzaG9waWZ5LmNv
26
+ bTAdBgNVHRIEFjAUgRJhZG1pbnNAc2hvcGlmeS5jb20wDQYJKoZIhvcNAQEFBQAD
27
+ ggEBADkK9aj5T0HPExsov4EoMWFnO+G7RQ28C30VAfKxnL2UxG6i4XMHVs6Xi94h
28
+ qXFw1ec9Y2eDUqaolT3bviOk9BB197+A8Vz/k7MC6ci2NE+yDDB7HAC8zU6LAx8Y
29
+ Iqvw7B/PSZ/pz4bUVFlTATif4mi1vO3lidRkdHRtM7UePSn2rUpOi0gtXBP3bLu5
30
+ YjHJN7wx5cugMEyroKITG5gL0Nxtu21qtOlHX4Hc4KdE2JqzCPOsS4zsZGhgwhPs
31
+ fl3hbtVFTqbOlwL9vy1fudXcolIE/ZTcxQ+er07ZFZdKCXayR9PPs64heamfn0fp
32
+ TConQSX2BnZdhIEYW+cKzEC/bLc=
33
+ -----END CERTIFICATE-----
34
+ date: 2015-01-07 00:00:00.000000000 Z
13
35
  dependencies:
14
36
  - !ruby/object:Gem::Dependency
15
37
  name: bundler
@@ -72,6 +94,7 @@ files:
72
94
  - lib/toxiproxy/toxic_collection.rb
73
95
  - lib/toxiproxy/version.rb
74
96
  - shipit.rubygems.yml
97
+ - test/fixtures/toxiproxy.json
75
98
  - test/test_helper.rb
76
99
  - test/toxiproxy_test.rb
77
100
  - toxiproxy.gemspec
@@ -99,6 +122,4 @@ rubygems_version: 2.2.2
99
122
  signing_key:
100
123
  specification_version: 4
101
124
  summary: Ruby library for Toxiproxy
102
- test_files:
103
- - test/test_helper.rb
104
- - test/toxiproxy_test.rb
125
+ test_files: []
metadata.gz.sig ADDED
Binary file