Current section
Files
Jump to
Current section
Files
lib/wobserver/util/node/discovery.ex
defmodule Wobserver.Util.Node.Discovery do @moduledoc ~S""" Helps discovering other nodes to connect to. The method used can be set in the config file by setting: ```elixir config :wobserver, discovery: :dns, ``` The following methods can be used: (default: `:none`) - `:none`, just returns the local node. - `:dns`, use DNS to search for other nodes. The option `discovery_search` needs to be set to filter entries. - `:custom`, a function as String. # Example config No discovery: (is default) ```elixir config :wobserver, port: 80, discovery: :none ``` Using dns as discovery service: ```elixir config :wobserver, port: 80, discovery: :dns, discovery_search: "google.nl" ``` Using a custom function: ```elixir config :wobserver, port: 80, discovery: :custom, discovery_search: &MyApp.CustomDiscovery.discover/0 ``` Using a anonymous function: ```elixir config :wobserver, port: 80, discovery: :custom, discovery_search: fn -> [] end ``` """ alias Wobserver.Util.Node.Remote # Finding @doc ~S""" Searches for a node in the discovered nodes. The `search` will be used to filter through names, hosts and host:port combinations. The special values for search are: - `local`, will always return the local node. """ @spec find(String.t()) :: :local | :unknown | {:remote, Remote.t()} def find("local"), do: :local def find(search) do found_node = Enum.find(discover(), fn %{name: name, host: host, port: port} -> search == name || search == host || search == "#{host}:#{port}" end) cond do found_node == nil -> :unknown local?(found_node) -> :local true -> {:remote, found_node} end end @doc ~S""" Retuns the local node. """ def local do discover() |> Enum.find(fn %{local?: is_local} -> is_local end) end # Discoverying @doc ~S""" Discovers other nodes to connect to. The method used can be set in the config file by setting: config :wobserver, discovery: :dns, The following methods can be used: (default: `:none`) - `:none`, just returns the local node. - `:dns`, use DNS to search for other nodes. The option `discovery_search` needs to be set to filter entries. - `:custom`, a function as String. # Example config No discovery: (is default) config :wobserver, discovery: :none Using dns: config :wobserver, discovery: :dns, discovery_search: "google.nl" Using a custom function: config :wobserver, discovery: :custom, discovery_search: "&MyApp.CustomDiscovery.discover/0" """ @spec discover :: list(Remote.t()) def discover do nodes = :wobserver |> Application.get_env(:discovery, :none) |> discovery_call case Enum.find(nodes, fn %{local?: is_local} -> is_local end) do nil -> discovery_call(:none) ++ nodes _ -> nodes end end @spec discovery_call(:dns) :: list(Remote.t()) defp discovery_call(:dns), do: dns_discover(Application.get_env(:wobserver, :discovery_search, nil)) @spec discovery_call(:custom) :: list(Remote.t()) defp discovery_call(:custom) do method = Application.get_env(:wobserver, :discovery_search, fn -> [] end) cond do is_binary(method) -> {call, []} = Code.eval_string(method) call.() is_function(method) -> method.() true -> [] end end @spec discovery_call(:none) :: list(Remote.t()) defp discovery_call(:none) do [ %Remote{ name: get_local_ip(), host: get_local_ip(), port: WobserverNG.Application.port(), local?: true } ] end @spec dns_discover(search :: String.t()) :: list(Remote.t()) defp dns_discover(search) when is_binary(search) do search |> String.to_charlist() |> :inet_res.lookup(:in, :a) |> Enum.map(&dns_to_node/1) end defp dns_discover(_), do: [] # Helpers defp local?(%{name: "local"}), do: true defp local?(%{host: "127.0.0.1", port: port}), do: port == WobserverNG.Application.port() defp local?(%{host: ip, port: port}), do: ip == get_local_ip() && port == WobserverNG.Application.port() defp local?(_), do: false defp ip_to_string({ip1, ip2, ip3, ip4}), do: "#{ip1}.#{ip2}.#{ip3}.#{ip4}" defp dns_to_node(ip) do remote_ip = ip_to_string(ip) remote = %Remote{ name: remote_ip, host: remote_ip, port: WobserverNG.Application.port(), local?: false } %Remote{remote | local?: local?(remote)} end defp get_local_ip do # with {:ok, ips} <- :inet.getif(), # {ip, _, _} <- List.first(ips), # do: ip_to_string(ip) {:ok, ips} = :inet.getif() {ip, _, _} = List.first(ips) ip_to_string(ip) endend