defmodule Remote do
node_str = System.get_env("REMOTE")
node = String.to_atom(node_str)
[host, _] = String.split(node_str, "@")
if not Node.connect(node), do: raise "Cannot connect to #{node}"
IO.puts("Connected to #{inspect Node.list(:hidden)}")
:persistent_term.put(:remote_node, node)
:persistent_term.put(:remote_host, host)
ebins = Path.join(System.get_env("ROOT"), "_build/dev/lib/**/ebin") |> Path.expand()
IO.puts("Adding code paths in in #{ebins}")
for dir <- Path.wildcard(ebins),
do: :code.add_pathz(String.to_charlist(dir))
deps = Path.join(System.get_env("ROOT"), "deps/*") |> Path.expand()
deps = for dir <- Path.wildcard(deps), do: Path.basename(dir)
IO.puts("Loading modules not in deps")
for dir <- Path.wildcard(ebins) do
["ebin", base| _] = Path.split(dir) |> Enum.reverse()
if not Enum.member?(deps, base) do
{:ok, files} = File.ls(dir)
for file <- files do
if Path.extname(file) == ".beam" do
module =
Path.join(dir, file)
|> String.to_charlist()
|> :beam_lib.info()
|> Keyword.fetch!(:module)
Code.ensure_loaded!(module)
end
end
end
end
def get_node(), do: :persistent_term.get(:remote_node)
def get_all_nodes(), do: :rpc.call(get_node(), :erlang, :nodes, [])
# get all nodes having the same host
def get_all_hosts() do
node = get_node()
host = :persistent_term.get(:remote_host)
get_all_nodes() |>
Enum.reduce([node], fn node, acc ->
case to_string(node) |> String.split("@") do
[^host, _] -> [node | acc]
_ -> acc
end
end)
end
def show(module) do
show_code_local(module)
show_code_remote(get_node(), module)
end
def show_all(module) do
show_code_local(module)
Enum.each(get_all_hosts(), fn node -> show_code_remote(node, module) end)
end
def load(module) do
show(module)
load_code(get_node(), module)
end
def load_all(module) do
Enum.each(get_all_hosts(), fn node -> show_code_remote(node, module) end)
show_code_local(module)
Enum.each(get_all_hosts(), fn node -> load_code(node, module) end)
end
def revert(module) do
show(module)
revert_code(get_node(), module)
show(module)
end
def revert_all(module) do
show_all(module)
Enum.each(get_all_hosts(), fn node -> revert_code(node, module) end)
show_all(module)
end
defp show_code_local(module) do
:code.purge(module)
:code.load_file(module)
IO.puts("LOCAL version is: #{inspect :erlang.phash2(module.module_info(:md5))}")
end
defp show_code_remote(node, module) do
remote = :rpc.call(node, module, :module_info, [:md5])
IO.puts("REMOTE version is: #{inspect :erlang.phash2(remote)} (#{inspect node})")
end
defp load_code(node, module) do
case :code.get_object_code(module) do
{^module, bin, fname} ->
case :rpc.call(node, :code, :load_binary, [module, fname, bin]) do
{:module, ^module} ->
show_code_remote(node, module)
other ->
other
end
other ->
{:error, other}
end
end
def revert_code(node, module) do
:rpc.call(node, :code, :purge, [module])
:rpc.call(node, :code, :get_object_code, [module])
end
end