toxiproxy 0.0.2 → 0.1.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: 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