ToxiproxyEx (toxiproxy_ex v1.0.0) View Source
ToxiproxyEx is an Elixir API client for the resilience testing tool Toxiproxy.
Toxiproxy is a proxy to simulate network and system conditions. The Elixir API aims to make it simple to write tests that ensure your application behaves appropriately under harsh conditions. Before you can use the Elixir library, you need to read the Usage section of the Toxiproxy README.
Usage
The Elixir client communicates with the Toxiproxy daemon via HTTP on http://127.0.0.1:8474
.
For example, to simulate 1000ms latency on a database server you can use the
latency
toxic with the latency
argument (see the Toxiproxy project for a
list of all toxics):
ToxiproxyEx.get!(:mysql_master)
|> ToxiproxyEx.toxic(:latency, latency: 1000)
|> ToxiproxyEx.apply!(fn ->
Repo.all(Shop) # this took at least 1s
end)
You can also take an endpoint down for the duration of a function at the TCP level:
ToxiproxyEx.get!(:mysql_master)
|> ToxiproxyEx.down!(fn ->
Repo.all(Shop) # this'll raise
end)
If you want to simulate all your Redis instances being down:
ToxiproxyEx.grep!(~r/redis/)
|> ToxiproxyEx.down!(fn ->
# any redis call will fail
end)
If you want to simulate that your cache server is slow at incoming network (upstream), but fast at outgoing (downstream), you can apply a toxic to just the upstream:
ToxiproxyEx.get!(:cache)
|> ToxiproxyEx.upstream(:latency, latency: 1000)
|> ToxiproxyEx.apply!(fn ->
Cache.get(:omg) # will take at least a second
end)
By default the toxic is applied to the downstream connection, you can be explicit and compose them:
ToxiproxyEx.grep!(~r/redis/)
|> ToxiproxyEx.upstream(:slow_close, delay: 100)
|> ToxiproxyEx.downstream(:latency, jitter: 300)
|> ToxiproxyEx.apply!(fn ->
# all redises are now slow at responding and closing
end)
See the Toxiproxy README for a list of toxics.
Populate
To populate Toxiproxy pass the proxy configurations to ToxiproxyEx.populate
:
ToxiproxyEx.populate!([
%{
name: "mysql_master",
listen: "localhost:21212",
upstream: "localhost:3306",
},
%{
name: "mysql_read_only",
listen: "localhost:21213",
upstream: "localhost:3306",
}
])
This will create the proxies passed, or replace the proxies if they already exist in Toxiproxy.
It's recommended to do this early as early in boot as possible, see the
Toxiproxy README. If you have many
proxies, we recommend storing the Toxiproxy configs in a configuration file and
deserializing it into ToxiproxyEx.populate
.
Error Handling
This library made the choice to use exceptions on the public API methods to signal errors.
This was chosen since this is a library meant to be used in testing code only, where you want test cases to fail if your set assumptions are not met. In this sense setting assumptions that will not be met (toxiproxy-server is not running, passing invalid configurations) is considered to be a developer error and should be fixed rather than handled in code.
Server Errors
If any API interaction with toxiproxy fails, a ServerError
will be raised.
Client Errors
If you miss-configure toxiproxy via the elixir API, an ArgumentError
will be raised.
Link to this section Summary
Types
A hostname or IP address including a port number, e.g. localhost:4539
.
A proxy that intercepts traffic to and from an upstream server.
A map containing fields required to setup a proxy. Designed to be used with ToxiproxyEx.populate!/1
.
A collection of proxies.
Functions
Retrieves a list of all proxies from the toxiproxy server.
Applies all toxics previously defined on the list of proxies during the duration of the given function.
Creates a proxy on the toxiproxy server.
Deletes one or multiple proxies on the toxiproxy server.
Takes down the proxy or the list of proxies during the duration of the given function.
Adds an downstream toxic to the proxy or list of proxies that will be enabled when passed to ToxiproxyEx.apply!/2
.
Retrieves a proxy from the toxiproxy server.
Retrieves a list of proxies from the toxiproxy server where the name matches the specificed regex.
Creates proxies based on the passed data. This is usefull to quickly create multiple proxies based on hardcoded value or values read from external sources such as a config file.
Re-enables are proxies and disables all toxics on toxiproxy.
Adds an upstream toxic to the proxy or list of proxies that will be enabled when passed to ToxiproxyEx.apply!/2
.
Gets the version of the running toxiproxy server.
Link to this section Types
Specs
host_with_port() :: String.t()
A hostname or IP address including a port number, e.g. localhost:4539
.
Specs
proxy()
A proxy that intercepts traffic to and from an upstream server.
Specs
proxy_map() :: %{ :name => String.t(), :upstream => host_with_port(), optional(:listen) => host_with_port(), optional(:enabled) => true | false }
A map containing fields required to setup a proxy. Designed to be used with ToxiproxyEx.populate!/1
.
Specs
toxic_collection()
A collection of proxies.
Link to this section Functions
Specs
all!() :: toxic_collection()
Retrieves a list of all proxies from the toxiproxy server.
Raises ToxiproxyEx.ServerError
if the list of proxies could not be retrieved.
Examples
Retrievs a proxy:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> ToxiproxyEx.create!(upstream: "localhost:3307", name: "test_redis_master")
iex> ToxiproxyEx.all!()
Specs
apply!(toxic_collection(), (() -> any())) :: :ok
Applies all toxics previously defined on the list of proxies during the duration of the given function.
Raises ToxiproxyEx.ServerError
if the toxics could not be enabled and disabled again on the server.
Examples
Add toxics and apply them toxic to a single proxy:
iex> proxy = ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> proxies = ToxiproxyEx.downstream(proxy, :slow_close, delay: 100)
iex> proxies = ToxiproxyEx.downstream(proxies, :latency, jitter: 300)
iex> ToxiproxyEx.apply!(proxies, fn ->
...> # All calls to mysql master are now slow at responding and closing.
...> nil
...> end)
Add toxics and apply them toxic to a list of proxies:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_follower")
iex> proxies = ToxiproxyEx.all!()
iex> proxies = ToxiproxyEx.downstream(proxies, :slow_close, delay: 100)
iex> proxies = ToxiproxyEx.downstream(proxies, :latency, jitter: 300)
iex> ToxiproxyEx.apply!(proxies, fn ->
...> # All calls to mysql master and follower are now slow at responding and closing.
...> nil
...> end)
Specs
create!( upstream: host_with_port(), name: String.t() | atom(), listen: host_with_port() | nil, enabled: true | false | nil ) :: proxy()
Creates a proxy on the toxiproxy server.
Raises ToxiproxyEx.ServerError
if the creation fails.
Examples
Create a new proxy:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
Create a new proxy that listens on a specific port:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", listen: "localhost:5555", name: "test_mysql_master")
Create a new proxy that is disabled by default:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master", enabled: false)
Specs
destroy!(proxy() | toxic_collection()) :: :ok
Deletes one or multiple proxies on the toxiproxy server.
Raises ToxiproxyEx.ServerError
if the deletion fails.
Examples
Destroy a single proxy:
iex> ToxiproxyEx.create!(upstream: "localhost:3456", name: :test_mysql_master)
iex> proxy = ToxiproxyEx.get!(:test_mysql_master)
iex> ToxiproxyEx.destroy!(proxy)
:ok
Destroy all proxies:
iex> ToxiproxyEx.create!(upstream: "localhost:3456", name: :test_mysql_master)
iex> proxies = ToxiproxyEx.all!()
iex> ToxiproxyEx.destroy!(proxies)
:ok
Specs
down!(toxic_collection(), (() -> any())) :: :ok
Takes down the proxy or the list of proxies during the duration of the given function.
Raises ToxiproxyEx.ServerError
if the proxy or list of proxies could not have been disabled and enabled again on the server.
Examples
Take down a single proxy:
iex> proxy = ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> ToxiproxyEx.down!(proxy, fn ->
...> # Takes mysql master down.
...> nil
...> end)
Take down a list of proxies:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_follower")
iex> proxies = ToxiproxyEx.all!()
iex> ToxiproxyEx.down!(proxies, fn ->
...> # Takes mysql master and follower down.
...> nil
...> end)
Specs
downstream(proxy() | toxic_collection(), atom(), []) :: toxic_collection()
Adds an downstream toxic to the proxy or list of proxies that will be enabled when passed to ToxiproxyEx.apply!/2
.
Examples
Add an downstream toxic to a proxy:
iex> proxy = ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> proxies = ToxiproxyEx.downstream(proxy, :latency, latency: 1000)
iex> ToxiproxyEx.apply!(proxies, fn ->
...> # Do some testing
...> nil
...> end)
Add an downstream toxic to a list of proxies:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> ToxiproxyEx.create!(upstream: "localhost:3307", name: "test_mysql_follower")
iex> proxies = ToxiproxyEx.all!()
iex> proxies = ToxiproxyEx.downstream(proxies, :latency, latency: 1000)
iex> ToxiproxyEx.apply!(proxies, fn ->
...> # Do some testing
...> nil
...> end)
Specs
Retrieves a proxy from the toxiproxy server.
Raises ToxiproxyEx.ServerError
if the proxy could not be retrieved.
Raises ArgumentError
if the proxy does not exist.
Examples
Retrievs a proxy:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> ToxiproxyEx.get!(:test_mysql_master)
Specs
grep!(Regex.t()) :: toxic_collection()
Retrieves a list of proxies from the toxiproxy server where the name matches the specificed regex.
Raises ToxiproxyEx.ServerError
if the list of proxies could not be retrieved.
Raises ArgumentError
if no proxy matching the specified regex does exist.
Examples
Retrievs a proxy:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> ToxiproxyEx.create!(upstream: "localhost:3307", name: "test_mysql_follower")
iex> ToxiproxyEx.create!(upstream: "localhost:3308", name: "test_redis_master")
iex> ToxiproxyEx.grep!(~r/master/)
Specs
populate!([proxy_map()]) :: toxic_collection()
Creates proxies based on the passed data. This is usefull to quickly create multiple proxies based on hardcoded value or values read from external sources such as a config file.
Nonexisting proxies will be created and existing ones will be updated to match the passed data.
Raises ToxiproxyEx.ServerError
if the proxies could not have been created on the server.
Examples
Creating proxies:
iex> ToxiproxyEx.populate!([
...> %{name: "test_mysql_master", upstream: "localhost:5765"},
...> %{name: "test_mysql_follower", upstream: "localhost:5766", enabled: false}
...> ])
Specs
reset!() :: :ok
Re-enables are proxies and disables all toxics on toxiproxy.
Raises ToxiproxyEx.ServerError
if the server could not have been reset.
Examples
Reset toxiproxy:
iex> ToxiproxyEx.reset!()
:ok
Specs
toxic(proxy() | toxic_collection(), atom(), []) :: toxic_collection()
Alias for ToxiproxyEx.downstream/3
.
Specs
toxicate(proxy() | toxic_collection(), atom(), []) :: toxic_collection()
Alias for ToxiproxyEx.downstream/3
.
Specs
upstream(proxy() | toxic_collection(), atom(), []) :: toxic_collection()
Adds an upstream toxic to the proxy or list of proxies that will be enabled when passed to ToxiproxyEx.apply!/2
.
Examples
Add an upstream toxic to a proxy:
iex> proxy = ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> proxies = ToxiproxyEx.upstream(proxy, :latency, latency: 1000)
iex> ToxiproxyEx.apply!(proxies, fn ->
...> # Do some testing
...> nil
...> end)
Add an upstream toxic to a list of proxies:
iex> ToxiproxyEx.create!(upstream: "localhost:3306", name: "test_mysql_master")
iex> ToxiproxyEx.create!(upstream: "localhost:3307", name: "test_mysql_follower")
iex> proxies = ToxiproxyEx.all!()
iex> proxies = ToxiproxyEx.upstream(proxies, :latency, latency: 1000)
iex> ToxiproxyEx.apply!(proxies, fn ->
...> # Do some testing
...> nil
...> end)
Specs
version!() :: :ok
Gets the version of the running toxiproxy server.
Raises ToxiproxyEx.ServerError
if the version could not have been fetched from the server.
Examples
Get running toxiproxy version:
iex> ToxiproxyEx.version!()
"2.1.2"