defmodule Runbox.ScenarioRelease do
@moduledoc """
Module for inspecting a scenario release.
"""
alias Runbox.Scenario
alias Runbox.ScenarioTemplate
alias Runbox.Slave
defmodule SlaveFunc do
require Logger
@moduledoc """
Functions which are called on the scenario slave via RPC from the master
node.
"""
alias Runbox.Scenario.TemplateInspector
@spec release_info([module] | nil) :: [Scenario.t()]
def release_info(modules \\ nil) do
modules_to_scenarios(modules || release_modules())
end
@doc """
Returns list of scenarios in release.
"""
@spec scenarios :: [scenario_id :: String.t()]
def scenarios do
release_modules()
|> Enum.filter(&Scenario.manifest_module?/1)
|> Enum.flat_map(fn manifest_mod ->
case scenario_manifest_info(manifest_mod) do
{:ok, manifest} ->
[manifest.id]
{:error, _} ->
Logger.warning("Ignoring manifest module #{inspect(manifest_mod)}")
[]
end
end)
end
defp release_modules do
scenario_app = Application.fetch_env!(:runbox, :scenario_app)
case :application.get_key(scenario_app, :modules) do
{:ok, modules} -> modules
:undefined -> []
end
end
defp modules_to_scenarios(modules) do
modules
|> Enum.filter(&Scenario.manifest_module?/1)
|> Enum.flat_map(fn manifest_mod ->
case scenario_manifest_info(manifest_mod) do
{:ok, manifest} ->
opts = scenario_opts(manifest_mod)
templates = scenario_templates(modules, manifest_mod, manifest)
[
%Scenario{
manifest: manifest,
opts: opts,
templates: templates
}
]
{:error, _} ->
Logger.warning("Ignoring manifest module #{inspect(manifest_mod)}")
[]
end
end)
end
defp scenario_templates(modules, manifest_mod, manifest) do
template_modules = Enum.filter(modules, &Scenario.template_module?(&1, manifest_mod))
%{template_inspector: inspector_mod} = Scenario.get_impl_for(manifest.type)
Enum.flat_map(template_modules, fn mod ->
if valid_template?(inspector_mod, mod) do
info = template_info(inspector_mod, mod)
[%ScenarioTemplate{module: mod, info: info}]
else
[]
end
end)
end
defp scenario_manifest_info(manifest_mod) do
{:ok, manifest_mod.get_info()}
catch
kind, value ->
Logger.warning(
"Error reading info of manifest #{inspect(manifest_mod)}: #{inspect({kind, value})}"
)
{:error, value}
end
defp scenario_opts(manifest_mod) do
if Code.ensure_loaded?(manifest_mod) && function_exported?(manifest_mod, :on_start, 0) do
%{on_start: {manifest_mod, :on_start, []}}
else
%{}
end
end
defp valid_template?(inspector_mod, template_mod) do
TemplateInspector.valid_template?(inspector_mod, template_mod)
end
defp template_info(inspector_mod, template_mod) do
TemplateInspector.template_info(inspector_mod, template_mod)
end
end
@doc """
Retrieves information about scenarios in a release.
"""
@spec release_info(String.t()) :: {:ok, [Scenario.t()]} | {:error, term()}
def release_info(release_dir) do
Slave.with_slave(release_dir, fn slave ->
Slave.call(slave, SlaveFunc, :release_info, [])
end)
end
end