Packages

Fault injection library in Elixir

Current section

Files

Jump to
faultex lib faultex.ex
Raw

lib/faultex.ex

defmodule Faultex do
@moduledoc """
"""
defstruct [
:id,
:disable,
:host,
:method,
:path_match,
:vars,
:headers,
:percentage,
:resp_status,
:resp_headers,
:resp_handler,
:resp_body,
:resp_delay,
:params_match
]
defmacro __before_compile__(env) do
injectors = Module.get_attribute(env.module, :__faultex_injectors__)
for injector <- Faultex.build_injectors(injectors) do
host = injector.host
method = injector.method
path_match = injector.path_match
headers = injector.headers || []
percentage = injector.percentage
disable = injector.disable || false
quote do
def match(
unquote(to_underscore(host)),
unquote(to_underscore(method)),
unquote(to_underscore(path_match)),
req_headers
) do
disabled? = Application.get_env(:faultex, :disable, false) || unquote(disable)
roll = Faultex.roll(unquote(percentage))
match_headers? = Faultex.match_req_headers?(req_headers, unquote(headers))
if roll and not disabled? and match_headers? do
unquote(Macro.escape(injector))
else
:pass
end
end
end
end
end
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
injectors = Keyword.get(opts, :injectors, [])
Module.put_attribute(__MODULE__, :__faultex_injectors__, injectors)
@before_compile Faultex
def match(host, method, path_match, req_headers, injector) do
# TODO host, method, path, headers のマッチをやる
disabled? =
Application.get_env(:injector, :disable, false) ||
(Map.get(injector, :disable) || false)
roll = Faultex.roll(injector.percentage)
match_headers? = Faultex.match_req_headers?(req_headers, injector.headers)
if roll and not disabled? and match_headers? do
injector
else
:pass
end
end
def do_match(host, method, path_match, req_headers, injector) do
case match(host, method, path_match, req_headers) do
%Faultex{} = injector ->
injector
_ ->
host_match? = Faultex.host_match?(host, injector.host)
method_match? = Faultex.method_match?(method, injector.method)
path_match? = Faultex.path_match?(method, injector.path)
req_headers_match? = Faultex.match_req_headers?(req_headers, injector.headers)
disabled? =
Application.get_env(:injector, :disable, false) ||
(Map.get(injector, :disable) || false)
roll = Faultex.roll(injector.percentage)
if host_match? and method_match? and path_match? and req_headers_match? and
not disabled? and roll do
injector
else
:pass
end
end
end
def get_injectors(), do: @__faultex_injectors__
end
end
# Generate struct for pattern match from config.exs
def build_injectors(injectors) do
Enum.map(injectors, fn injector ->
do_build_injector(injector)
end) ++
[
%Faultex{
disable: true
}
]
end
defp do_build_injector(injector_id) when is_atom(injector_id) do
injector = Application.fetch_env!(:faultex, injector_id)
path = Keyword.get(injector, :path, "*")
host = Keyword.get(injector, :host, "*")
method = Keyword.get(injector, :method, "GET")
headers = Keyword.get(injector, :headers, [])
percentage = Keyword.get(injector, :percentage, 100)
resp_body = Keyword.get(injector, :resp_body, "")
resp_status = Keyword.get(injector, :resp_status, 200)
resp_headers = Keyword.get(injector, :resp_headers, [])
resp_handler = Keyword.get(injector, :resp_handler, nil)
resp_delay = Keyword.get(injector, :resp_delay, 0)
disable = Keyword.get(injector, :disable, false)
{vars, path_match} = Plug.Router.Utils.build_path_match(path)
params_match = Plug.Router.Utils.build_path_params_match(vars)
%Faultex{
id: injector,
disable: disable,
params_match: params_match,
vars: vars,
host: host,
method: method,
path_match: path_match,
percentage: percentage,
headers: headers,
resp_status: resp_status,
resp_body: resp_body,
resp_headers: resp_headers,
resp_delay: resp_delay,
resp_handler: resp_handler
}
end
defp do_build_injector(injector) when is_map(injector) do
path = Map.get(injector, :path, "*")
host = Map.get(injector, :host, "*")
method = Map.get(injector, :method, "GET")
headers = Map.get(injector, :headers, [])
percentage = Map.get(injector, :percentage, 100)
resp_body = Map.get(injector, :resp_body, "")
resp_status = Map.get(injector, :resp_status, 200)
resp_headers = Map.get(injector, :resp_headers, [])
resp_handler = Map.get(injector, :resp_handler, nil)
resp_delay = Map.get(injector, :resp_delay, 0)
disable = Map.get(injector, :disable, false)
{vars, path_match} = Plug.Router.Utils.build_path_match(path)
params_match = Plug.Router.Utils.build_path_params_match(vars)
%Faultex{
id: injector,
disable: disable,
params_match: params_match,
vars: vars,
host: host,
method: method,
path_match: path_match,
percentage: percentage,
headers: headers,
resp_status: resp_status,
resp_body: resp_body,
resp_headers: resp_headers,
resp_delay: resp_delay,
resp_handler: resp_handler
}
end
def host_match?(_host, nil), do: true
def host_match?(_host, "*"), do: true
def host_match?(host, expected), do: host == expected
def method_match?(_method, nil), do: true
def method_match?(_method, "*"), do: true
def method_match?(method, expected), do: method == expected
def path_match?(_path, nil), do: true
def path_match?(_path, "*"), do: true
# FIXME
def path_match?(path, expected) do
path == expected
end
defp to_underscore(nil) do
quote do: _
end
defp to_underscore("*") do
quote do: _
end
defp to_underscore(any) do
any
end
def match_req_headers?(req_headers, headers) do
Enum.all?(headers, fn header ->
header in req_headers
end)
end
def roll(100), do: true
def roll(percentage), do: :rand.uniform(100) < percentage
end