Packages
altworx_runbox
23.0.0
25.0.0
24.0.0
23.1.0
23.0.0
22.2.0
22.1.0
22.0.0
21.2.0
21.1.2
21.1.1
21.1.0
21.0.0
20.0.0
19.0.0
18.0.0
17.2.0
17.1.0
17.0.1
17.0.0
16.2.0
16.1.0
16.0.0
15.0.0
14.1.0
14.0.1
14.0.0
13.0.3
13.0.2
13.0.1
13.0.0
12.1.0
12.0.0
11.0.1
11.0.0
10.0.0
9.0.0
8.0.0
7.0.1
7.0.0
6.0.0
5.0.0
4.0.0
3.0.0
2.1.0
2.0.0
1.4.1
1.4.0
1.3.0
1.2.0
1.1.0
1.0.0
0.1.3
0.1.2
0.1.1
0.1.0
Runbox is a library for running Altworx scenarios.
Current section
Files
Jump to
Current section
Files
lib/runbox/slave.ex
defmodule Runbox.Slave do
@moduledoc group: :internal
@moduledoc """
Utilities for starting a slave node with a scenario release and communicating
with the node.
"""
alias Runbox.Utils.Path, as: PathUtils
@doc """
Starts a slave node.
The `release_dir` is a directory with scenario release.
If `app_env` is set, given app environment variables are set on slave.
"""
@spec start(String.t(), [{Application.app(), [{Application.key(), Application.value()}]}]) ::
{:ok, pid(), node()} | {:error, term()}
def start(release_dir, app_env \\ []) do
host = slave_host() |> to_charlist()
slave_name = UUID.uuid4() |> to_charlist()
args =
[
["-setcookie", "#{Node.get_cookie()}"],
["-boot", boot_file(release_dir)],
["-boot_var", "RELEASE_LIB", release_lib(release_dir)],
["-config", config_file(release_dir)],
["-mode", "interactive"],
["-runbox", "mode", "slave"],
"-hidden",
erl_opt_runbox_scenario_config_dir(),
erl_opt_logger_console(),
erl_opt_runbox_altworx_root(),
# Limit number of concurrent ports/sockets
# https://www.erlang.org/doc/apps/erts/erl_cmd.html#max_ports
["+Q", "65536"]
]
|> List.flatten()
|> Enum.map(&to_charlist/1)
opts = %{name: slave_name, host: host, args: args}
case :peer.start_link(opts) do
{:ok, pid, slave} ->
with :ok <- check_runbox_started(slave),
:ok <- check_runbox_scenario_app_configured(slave),
:ok <- put_app_env(slave, app_env) do
{:ok, pid, slave}
else
{:error, _} = error ->
:ok = :peer.stop(pid)
error
end
{:error, _} = error ->
error
end
end
@doc """
Spawns a slave and executes the `fun`.
`fun` receives the slave node as a parameter. The slave is terminated after
the `fun` finishes.
"""
@spec with_slave(String.t(), (node() -> result)) :: {:ok, result} | {:error, term}
when result: any()
def with_slave(release_dir, fun) do
with {:ok, pid, slave} <- start(release_dir) do
try do
{:ok, fun.(slave)}
after
:ok = :peer.stop(pid)
end
end
end
@doc """
Calls a function on the slave.
Calls MFA on the `slave`. See `:erpc.call/4` for details.
"""
@spec call(node(), module(), atom(), list()) :: term()
def call(slave, m, f, a) do
:erpc.call(slave, m, f, a)
end
defp check_runbox_started(slave) do
apps = call(slave, Application, :started_applications, [])
if Enum.any?(apps, &match?({:runbox, _, _}, &1)) do
:ok
else
{:error, :runbox_not_running_on_slave}
end
end
defp check_runbox_scenario_app_configured(slave) do
case call(slave, Application, :fetch_env, [:runbox, :scenario_app]) do
{:ok, _} -> :ok
:error -> {:error, {:runbox_misconfigured, "Missing app config key :scenario_app"}}
end
end
defp put_app_env(slave, app_env) do
if app_env == [], do: :ok, else: call(slave, Application, :put_all_env, [app_env])
end
defp release_lib(release_dir) do
Path.join([release_dir, "lib"])
end
defp boot_file(release_dir) do
Path.join([release_dir, "releases", release_version(release_dir), "start"])
end
defp config_file(release_dir) do
Path.join([release_dir, "releases", release_version(release_dir), "sys"])
end
defp release_version(release_dir) do
data_file = Path.join([release_dir, "releases", "start_erl.data"])
[_, release_version] =
data_file
|> File.read!()
|> String.split(" ")
release_version
end
defp slave_host, do: this_host()
# The name of the host has to be derived from the node name. If taken from
# `:inet.gethostname`, it does not work inside docker.
defp this_host do
[_name, host] =
node()
|> Atom.to_string()
|> String.split("@")
host
end
defp erl_opt_runbox_scenario_config_dir do
scenario_config_dir = Application.fetch_env!(:runbox, :scenario_config_dir)
["-runbox", "scenario_config_dir", erl_format_term(scenario_config_dir)]
end
defp erl_opt_logger_console do
case Application.fetch_env(:logger, :default_formatter) do
{:ok, conf} -> ["-logger", "default_formatter", erl_format_term(conf)]
:error -> []
end
end
defp erl_opt_runbox_altworx_root do
env_var = PathUtils.altworx_root_env_var()
altworx_root = PathUtils.get_altworx_root()
["-runbox", erl_format_term(env_var), erl_format_term(altworx_root)]
end
defp erl_format_term(term) do
IO.chardata_to_string(:io_lib.format("~tw", [term]))
end
end