toxiproxy 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f2f83e60344122fe413e11816f5c976e46de7d6e
4
+ data.tar.gz: 52de814ecc4b5c472329179daf349599c9a5d15a
5
+ SHA512:
6
+ metadata.gz: 1ae9818932feabbb5982eb525d7dcfd1a03d20047d0e99d2cbd07e3b62d474285e118a29505802571c8132f595a5c014e0bf9327e9a0a6f8358db2836e37f8cb
7
+ data.tar.gz: a58e9df0b31c788e9b927fdf7c399621dce64e4f9dcb83e9670ea87ddcfe230a05eb2f3f2be29cf83e23d56ef2e5675a62f6210d66c1006030712cc34a654d36
@@ -0,0 +1,3 @@
1
+ vendor/
2
+ .bundle
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Shopify
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,50 @@
1
+ # toxiproxy-ruby
2
+
3
+ [Toxiproxy](https://github.com/shopify/toxiproxy) is a proxy to simulate network
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.
7
+
8
+ ```
9
+ gem install toxiproxy
10
+ ```
11
+
12
+ Make sure the Toxiproxy server is already running.
13
+
14
+ ## Usage
15
+
16
+ For example, to simulate 1000ms latency on a database server you can use the
17
+ `latency` toxic with the `latency` argument (see the Toxiproxy project for a
18
+ list of all toxics):
19
+
20
+ ```ruby
21
+ Toxiproxy[:mysql_master].downstream(:latency, latency: 1000) do
22
+ Shop.first # this took at least 1s
23
+ end
24
+ ```
25
+
26
+ You can also take an endpoint down for the duration of a block at the TCP level:
27
+
28
+ ```ruby
29
+ Toxiproxy[:mysql_master].down do
30
+ Shop.first # this'll raise
31
+ end
32
+ ```
33
+
34
+ If you want to simulate all your Redis instances being down:
35
+
36
+ ```ruby
37
+ Toxiproxy[/redis/].down do
38
+ # any redis call will fail
39
+ end
40
+ ```
41
+
42
+ If you want to simulate that your cache server is slow at incoming network
43
+ (upstream), but fast at outgoing (downstream), you can apply a toxic to just the
44
+ upstream:
45
+
46
+ ```ruby
47
+ Toxiproxy[:cache].upstream(:latency, latency: 1000) do
48
+ Cache.get(:omg) # will take at least a second
49
+ end
50
+ ```
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ desc 'Default: run unit tests.'
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'lib'
9
+ t.libs << 'test'
10
+ t.pattern = 'test/*_test.rb'
11
+ t.verbose = true
12
+ end
@@ -0,0 +1,8 @@
1
+ #!/bin/bash -e
2
+
3
+ VERSION='fad1365c087d6ad53944c8201a6131cb'
4
+
5
+ echo "[start toxiproxy]"
6
+ curl --silent http://shopify-vagrant.s3.amazonaws.com/toxiproxy/toxiproxy-$VERSION -o ./bin/toxiproxy
7
+ chmod +x ./bin/toxiproxy
8
+ nohup bash -c "./bin/toxiproxy > ${CIRCLE_ARTIFACTS}/toxiproxy.log 2>&1 &"
@@ -0,0 +1,7 @@
1
+ dependencies:
2
+ override:
3
+ - bundle install
4
+ - ./bin/start-toxiproxy.sh
5
+ test:
6
+ override:
7
+ - bundle exec rake test
@@ -0,0 +1,179 @@
1
+ require "json"
2
+ require "uri"
3
+ require "net/http"
4
+
5
+ require "toxiproxy/collection"
6
+ require "toxiproxy/toxic"
7
+ require "toxiproxy/toxic_collection"
8
+
9
+ class Toxiproxy
10
+ URI = ::URI.parse("http://127.0.0.1:8474")
11
+ VALID_DIRECTIONS = [:upstream, :downstream]
12
+
13
+ class NotFound < StandardError; end
14
+ class ProxyExists < StandardError; end
15
+ class InvalidToxic < StandardError; end
16
+
17
+ attr_reader :listen, :name
18
+
19
+ def initialize(options)
20
+ @upstream = options[:upstream]
21
+ @listen = options[:listen] || "localhost:0"
22
+ @name = options[:name]
23
+ end
24
+
25
+ # Forwardable doesn't support delegating class methods, so we resort to
26
+ # `define_method` to delegate from Toxiproxy to #all, and from there to the
27
+ # proxy collection.
28
+ class << self
29
+ Collection::METHODS.each do |method|
30
+ define_method(method) do |*args, &block|
31
+ self.all.send(method, *args, &block)
32
+ end
33
+ end
34
+ end
35
+
36
+ # Returns a collection of all currently active Toxiproxies.
37
+ def self.all
38
+ request = Net::HTTP::Get.new("/proxies")
39
+ response = http.request(request)
40
+ assert_response(response)
41
+
42
+ proxies = JSON.parse(response.body).map { |name, attrs|
43
+ self.new({
44
+ upstream: attrs["upstream"],
45
+ listen: attrs["listen"],
46
+ name: attrs["name"]
47
+ })
48
+ }
49
+
50
+ Collection.new(proxies)
51
+ end
52
+
53
+ # Convenience method to create a proxy.
54
+ def self.create(options)
55
+ self.new(options).create
56
+ end
57
+
58
+ # Find a single proxy by name.
59
+ def self.find_by_name(name = nil, &block)
60
+ proxy = self.all.find { |p| p.name == name.to_s }
61
+ raise NotFound, "#{name} not found in #{self.all.map(&:name).join(', ')}" unless proxy
62
+ proxy
63
+ end
64
+
65
+ # If given a regex, it'll use `grep` to return a Toxiproxy::Collection.
66
+ # Otherwise, it'll convert the passed object to a string and find the proxy by
67
+ # name.
68
+ def self.[](query)
69
+ return grep(query) if query.is_a?(Regexp)
70
+ find_by_name(query)
71
+ end
72
+
73
+ # Set an upstream toxic.
74
+ def upstream(toxic = nil, attrs = {})
75
+ return @upstream unless toxic
76
+
77
+ collection = ToxicCollection.new(self)
78
+ collection.upstream(toxic, attrs)
79
+ collection
80
+ end
81
+
82
+ # Set a downstream toxic.
83
+ def downstream(toxic, attrs = {})
84
+ collection = ToxicCollection.new(self)
85
+ collection.downstream(toxic, attrs)
86
+ collection
87
+ end
88
+
89
+ # Simulates the endpoint is down, by closing the connection and no
90
+ # longer accepting connections. This is useful to simulate critical system
91
+ # failure, such as a data store becoming completely unavailable.
92
+ def down(&block)
93
+ uptoxics = toxics(:upstream)
94
+ downtoxics = toxics(:downstream)
95
+ destroy
96
+ begin
97
+ yield
98
+ ensure
99
+ create
100
+ uptoxics.each(&:save)
101
+ downtoxics.each(&:save)
102
+ end
103
+ end
104
+
105
+ # Create a Toxiproxy, proxying traffic from `@listen` (optional argument to
106
+ # the constructor) to `@upstream`. `#down` `#upstream` or `#downstream` can at any time alter the health
107
+ # of this connection.
108
+ def create
109
+ request = Net::HTTP::Post.new("/proxies")
110
+
111
+ hash = {upstream: upstream, name: name, listen: listen}
112
+ request.body = hash.to_json
113
+
114
+ response = http.request(request)
115
+ assert_response(response)
116
+
117
+ new = JSON.parse(response.body)
118
+ @listen = new["listen"]
119
+
120
+ self
121
+ end
122
+
123
+ # Destroys a Toxiproxy.
124
+ def destroy
125
+ request = Net::HTTP::Delete.new("/proxies/#{name}")
126
+ response = http.request(request)
127
+ assert_response(response)
128
+ self
129
+ end
130
+
131
+ private
132
+
133
+ # Returns a collection of the current toxics for a direction.
134
+ def toxics(direction)
135
+ unless VALID_DIRECTIONS.include?(direction.to_sym)
136
+ raise InvalidToxic, "Toxic direction must be one of: [#{VALID_DIRECTIONS.join(', ')}], got: #{direction}"
137
+ end
138
+
139
+ request = Net::HTTP::Get.new("/proxies/#{name}/#{direction}/toxics")
140
+ response = http.request(request)
141
+ assert_response(response)
142
+
143
+ toxics = JSON.parse(response.body).map { |name, attrs|
144
+ Toxic.new({
145
+ name: name,
146
+ proxy: self,
147
+ direction: direction,
148
+ attrs: attrs
149
+ })
150
+ }
151
+
152
+ toxics
153
+ end
154
+
155
+ def self.http
156
+ @http ||= Net::HTTP.new(URI.host, URI.port)
157
+ end
158
+
159
+ def http
160
+ self.class.http
161
+ end
162
+
163
+ def self.assert_response(response)
164
+ case response
165
+ when Net::HTTPConflict
166
+ raise Toxiproxy::ProxyExists, response.body
167
+ when Net::HTTPNotFound
168
+ raise Toxiproxy::NotFound, response.body
169
+ when Net::HTTPBadRequest
170
+ raise Toxiproxy::InvalidToxic, response.body
171
+ else
172
+ response.value # raises if not OK
173
+ end
174
+ end
175
+
176
+ def assert_response(*args)
177
+ self.class.assert_response(*args)
178
+ end
179
+ end
@@ -0,0 +1,58 @@
1
+ require "forwardable"
2
+
3
+ class Toxiproxy
4
+ # ProxyCollection represents a set of proxies. This allows to easily perform
5
+ # actions on every proxy in the collection.
6
+ #
7
+ # Unfortunately, it doesn't implement all of Enumerable because there's no way
8
+ # to subclass an Array or include Enumerable for the methods to return a
9
+ # Collection instead of an Array (see MRI). Instead, we delegate methods where
10
+ # it doesn't matter and only allow the filtering methods that really make
11
+ # sense on a proxy collection.
12
+ class Collection
13
+ extend Forwardable
14
+
15
+ DELEGATED_METHODS = [:length, :size, :count, :find, :each, :map]
16
+ DEFINED_METHODS = [:select, :reject, :grep, :down]
17
+ METHODS = DEFINED_METHODS + DELEGATED_METHODS
18
+
19
+ def_delegators :@collection, *DELEGATED_METHODS
20
+
21
+ def initialize(collection)
22
+ @collection = collection
23
+ end
24
+
25
+ # Sets every proxy in the collection as down. For example:
26
+ #
27
+ # Toxiproxy.grep(/redis/).down { .. }
28
+ #
29
+ # Would simulate every Redis server being down for the duration of the
30
+ # block.
31
+ def down(*args, &block)
32
+ @collection.inject(block) { |nested, proxy|
33
+ -> { proxy.down(*args, &nested) }
34
+ }.call
35
+ end
36
+
37
+ # Destroys all toxiproxy's in the collection
38
+ def destroy
39
+ @collection.each(&:destroy)
40
+ end
41
+
42
+ def select(&block)
43
+ self.class.new(@collection.select(&block))
44
+ end
45
+
46
+ def reject(&block)
47
+ self.class.new(@collection.reject(&block))
48
+ end
49
+
50
+ # Grep allows easily selecting a subset of proxies, by returning a
51
+ # ProxyCollection with every proxy name matching the regex passed.
52
+ def grep(regex)
53
+ self.class.new(@collection.select { |proxy|
54
+ proxy.name =~ regex
55
+ })
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,47 @@
1
+ class Toxiproxy
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] || {}
11
+ end
12
+
13
+ def enabled?
14
+ attrs[:enabled]
15
+ end
16
+
17
+ def enable
18
+ attrs[:enabled] = true
19
+ save
20
+ end
21
+
22
+ def disable
23
+ attrs[:enabled] = false
24
+ save
25
+ end
26
+
27
+ def []=(name, value)
28
+ attrs[name] = value
29
+ end
30
+
31
+ def save
32
+ unless VALID_DIRECTIONS.include?(direction.to_sym)
33
+ raise InvalidToxic, "Toxic direction must be one of: [#{VALID_DIRECTIONS.join(', ')}], got: #{direction}"
34
+ end
35
+ request = Net::HTTP::Post.new("/proxies/#{proxy.name}/#{direction}/toxics/#{name}")
36
+
37
+ request.body = attrs.to_json
38
+
39
+ response = Toxiproxy.http.request(request)
40
+ Toxiproxy.assert_response(response)
41
+
42
+ @attrs = JSON.parse(response.body)
43
+
44
+ self
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,42 @@
1
+ class Toxiproxy
2
+ class ToxicCollection
3
+ extend Forwardable
4
+
5
+ attr_accessor :toxics
6
+ attr_reader :proxy
7
+
8
+ def_delegators :@toxics, :<<, :find
9
+
10
+ def initialize(proxy)
11
+ @proxy = proxy
12
+ @toxics = []
13
+ end
14
+
15
+ def apply(&block)
16
+ @toxics.each(&:enable)
17
+ yield
18
+ ensure
19
+ @toxics.each(&:disable)
20
+ end
21
+
22
+ def upstream(toxic_name, attrs = {})
23
+ toxics << Toxic.new(
24
+ name: toxic_name,
25
+ proxy: proxy,
26
+ direction: :upstream,
27
+ attrs: attrs
28
+ )
29
+ self
30
+ end
31
+
32
+ def downstream(toxic_name, attrs = {})
33
+ toxics << Toxic.new(
34
+ name: toxic_name,
35
+ proxy: proxy,
36
+ direction: :downstream,
37
+ attrs: attrs
38
+ )
39
+ self
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ class Toxiproxy
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,2 @@
1
+ fetch:
2
+ - fetch-gem-version toxiproxy Shopify/toxiproxy-ruby
@@ -0,0 +1,3 @@
1
+ require 'minitest'
2
+ require 'minitest/autorun'
3
+ require_relative "../lib/toxiproxy"
@@ -0,0 +1,209 @@
1
+ require 'test_helper'
2
+
3
+ class ToxiproxyTest < MiniTest::Unit::TestCase
4
+ def teardown
5
+ Toxiproxy.grep(/\Atest_/).each(&:destroy)
6
+ end
7
+
8
+ def test_create_proxy
9
+ proxy = Toxiproxy.create(upstream: "localhost:3306", name: "test_mysql_master")
10
+
11
+ assert_equal "localhost:3306", proxy.upstream
12
+ assert_equal "test_mysql_master", proxy.name
13
+ end
14
+
15
+ def test_create_and_find_proxy
16
+ proxy = Toxiproxy.create(upstream: "localhost:3306", name: "test_mysql_master")
17
+
18
+ assert_equal "localhost:3306", proxy.upstream
19
+ assert_equal "test_mysql_master", proxy.name
20
+
21
+ proxy = Toxiproxy[:test_mysql_master]
22
+
23
+ assert_equal "localhost:3306", proxy.upstream
24
+ assert_equal "test_mysql_master", proxy.name
25
+ end
26
+
27
+ def test_take_endpoint_down
28
+ with_tcpserver do |port|
29
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_rubby_server")
30
+ listen_addr = proxy.listen
31
+
32
+ proxy.down do
33
+ assert_proxy_unavailable proxy
34
+ end
35
+
36
+ assert_proxy_available proxy
37
+
38
+ assert_equal listen_addr, proxy.listen
39
+ end
40
+ end
41
+
42
+ def test_raises_when_proxy_doesnt_exist
43
+ assert_raises Toxiproxy::NotFound do
44
+ Toxiproxy[:does_not_exist]
45
+ end
46
+ end
47
+
48
+ def test_proxies_all_returns_proxy_collection
49
+ assert_instance_of Toxiproxy::Collection, Toxiproxy.all
50
+ end
51
+
52
+ def test_down_on_proxy_collection_disables_entire_collection
53
+ with_tcpserver do |port1|
54
+ with_tcpserver do |port2|
55
+ proxy1 = Toxiproxy.create(upstream: "localhost:#{port1}", name: "test_proxy1")
56
+ proxy2 = Toxiproxy.create(upstream: "localhost:#{port2}", name: "test_proxy2")
57
+
58
+ assert_proxy_available proxy2
59
+ assert_proxy_available proxy1
60
+
61
+ Toxiproxy.all.down do
62
+ assert_proxy_unavailable proxy1
63
+ assert_proxy_unavailable proxy2
64
+ end
65
+
66
+ assert_proxy_available proxy2
67
+ assert_proxy_available proxy1
68
+ end
69
+ end
70
+ end
71
+
72
+ def test_select_from_toxiproxy_collection
73
+ with_tcpserver do |port|
74
+ Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
75
+
76
+ proxies = Toxiproxy.select { |p| p.upstream == "localhost:#{port}" }
77
+
78
+ assert_equal 1, proxies.size
79
+ assert_instance_of Toxiproxy::Collection, proxies
80
+ end
81
+ end
82
+
83
+ def test_grep_returns_toxiproxy_collection
84
+ with_tcpserver do |port|
85
+ Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
86
+
87
+ proxies = Toxiproxy.grep(/\Atest/)
88
+
89
+ assert_equal 1, proxies.size
90
+ assert_instance_of Toxiproxy::Collection, proxies
91
+ end
92
+ end
93
+
94
+ def test_indexing_allows_regexp
95
+ with_tcpserver do |port|
96
+ Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
97
+
98
+ proxies = Toxiproxy[/\Atest/]
99
+
100
+ assert_equal 1, proxies.size
101
+ assert_instance_of Toxiproxy::Collection, proxies
102
+ end
103
+ end
104
+
105
+ def test_apply_upstream_toxic
106
+ $before = Time.now
107
+
108
+ with_tcpserver(receive: true) do |port|
109
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
110
+
111
+ proxy.upstream(:latency, latency: 100).apply do
112
+ before = Time.now
113
+
114
+ socket = connect_to_proxy(proxy)
115
+ socket.write("omg\n")
116
+ socket.flush
117
+ socket.gets
118
+
119
+ passed = Time.now - before
120
+
121
+ assert_in_delta passed, 0.100, 0.01
122
+ end
123
+ end
124
+ end
125
+
126
+ def test_apply_downstream_toxic
127
+ with_tcpserver(receive: true) do |port|
128
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
129
+
130
+ proxy.downstream(:latency, latency: 100).apply do
131
+ before = Time.now
132
+
133
+ socket = connect_to_proxy(proxy)
134
+ socket.write("omg\n")
135
+ socket.flush
136
+ socket.gets
137
+
138
+ passed = Time.now - before
139
+
140
+ assert_in_delta passed, 0.100, 0.01
141
+ end
142
+ end
143
+ end
144
+
145
+ def test_apply_prolong_toxics
146
+ with_tcpserver(receive: true) do |port|
147
+ proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")
148
+
149
+ proxy.upstream(:latency, latency: 100).downstream(:latency, latency: 100).apply do
150
+ before = Time.now
151
+
152
+ socket = connect_to_proxy(proxy)
153
+ socket.write("omg\n")
154
+ socket.flush
155
+ socket.gets
156
+
157
+ passed = Time.now - before
158
+
159
+ assert_in_delta passed, 0.200, 0.01
160
+ end
161
+ end
162
+ end
163
+
164
+ private
165
+
166
+ def assert_proxy_available(proxy)
167
+ connect_to_proxy proxy
168
+ end
169
+
170
+ def assert_proxy_unavailable(proxy)
171
+ assert_raises Errno::ECONNREFUSED do
172
+ connect_to_proxy proxy
173
+ end
174
+ end
175
+
176
+ def connect_to_proxy(proxy)
177
+ TCPSocket.new(*proxy.listen.split(":".freeze))
178
+ end
179
+
180
+ def with_tcpserver(receive = false, &block)
181
+ mon = Monitor.new
182
+ cond = mon.new_cond
183
+ port = nil
184
+
185
+ thread = Thread.new {
186
+ server = TCPServer.new("127.0.0.1", 0)
187
+ port = server.addr[1]
188
+ mon.synchronize { cond.signal }
189
+ loop do
190
+ client = server.accept
191
+
192
+ if receive
193
+ client.gets
194
+ client.write("omgs\n")
195
+ client.flush
196
+ end
197
+
198
+ client.close
199
+ end
200
+ server.close
201
+ }
202
+
203
+ mon.synchronize { cond.wait }
204
+
205
+ yield(port)
206
+ ensure
207
+ thread.kill
208
+ end
209
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path("../lib/toxiproxy/version", __FILE__)
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "toxiproxy"
5
+ spec.version = Toxiproxy::VERSION
6
+ spec.authors = ["Simon Eskildsen", "Jacob Wirth"]
7
+ spec.email = "simon.eskildsen@shopify.com"
8
+ spec.summary = "Ruby library for Toxiproxy"
9
+ spec.description = "A Ruby library for controlling Toxiproxy. Can be used in resiliency testing."
10
+ spec.homepage = "https://github.com/Shopify/toxiproxy"
11
+ spec.license = "MIT"
12
+
13
+ spec.files = `git ls-files`.split("\n")
14
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_development_dependency "bundler", "~> 1.3"
18
+ spec.add_development_dependency "minitest"
19
+ spec.add_development_dependency "rake"
20
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: toxiproxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Simon Eskildsen
8
+ - Jacob Wirth
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-11-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.3'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.3'
28
+ - !ruby/object:Gem::Dependency
29
+ name: minitest
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: A Ruby library for controlling Toxiproxy. Can be used in resiliency testing.
57
+ email: simon.eskildsen@shopify.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - bin/start-toxiproxy.sh
68
+ - circle.yml
69
+ - lib/toxiproxy.rb
70
+ - lib/toxiproxy/collection.rb
71
+ - lib/toxiproxy/toxic.rb
72
+ - lib/toxiproxy/toxic_collection.rb
73
+ - lib/toxiproxy/version.rb
74
+ - shipit.rubygems.yml
75
+ - test/test_helper.rb
76
+ - test/toxiproxy_test.rb
77
+ - toxiproxy.gemspec
78
+ homepage: https://github.com/Shopify/toxiproxy
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.2.2
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Ruby library for Toxiproxy
102
+ test_files:
103
+ - test/test_helper.rb
104
+ - test/toxiproxy_test.rb