Current section

Files

Jump to
node_activator lib node_activator.ex
Raw

lib/node_activator.ex

defmodule NodeActivator do
@moduledoc """
This module provides functionality to turn a non-distributed Erlang node into a distributed node.
It handles the entire process of node activation, including:
* Ensuring the EPMD (Erlang Port Mapper Daemon) is running
* Generating unique node names
* Starting the distributed node
* Providing proper error handling and logging
## Features
* Automatic EPMD activation
* Unique node name generation with customizable prefixes
* Idempotent operation (safe to call multiple times)
* Comprehensive error handling
* Detailed logging of node operations
## Usage
The main function `run/1` takes a prefix string and returns either:
* `{:ok, node()}` - The node was successfully started or was already running
* `{:error, reason}` - An error occurred during node activation
## Dependencies
* `epmd_up` - For EPMD management
* `get_host` - For hostname resolution
## Examples
# Start a new distributed node with prefix "myapp"
{:ok, node_name} = NodeActivator.run("myapp")
# => {:ok, :"myapp_abc123@hostname"}
# Safe to call multiple times
{:ok, same_node} = NodeActivator.run("myapp")
# => {:ok, :"myapp_abc123@hostname"}
"""
require Logger
@doc """
Turns a non-distributed node into a distributed node after ensuring
that the `epmd` operation system process is running. Returns the name of the
started distributed node. The node name will be generated by appending
a random string to the provided `node_name_prefix` string.
This function does nothing when the distribution has already
been started.
For more info, see `Node`.
## Examples
{:ok, node_name} = NodeActivator.run("foo")
"""
@spec run(binary()) :: {:ok, node()} | {:error, any()}
def run(node_name_prefix) do
if Node.alive?() do
{:ok, Node.self()}
else
start_distributed_node(node_name_prefix)
end
end
defp start_distributed_node(node_name_prefix) do
EpmdUp.activate()
case NodeActivator.Util.generate_node_name(node_name_prefix) do
{:ok, name} ->
Logger.info("starting node #{name}")
case Node.start(name) do
{:ok, _node} ->
Logger.info("Node #{name} has been launched.")
{:ok, Node.self()}
{:error, reason} ->
Logger.error("Node #{name} cannot be launched by #{inspect(reason)}.")
{:error, reason}
end
{:error, reason} ->
{:error, reason}
end
end
end