livebook/lib/livebook/application.ex
José Valim 69890cf43e
Allow errors on boot to be reported (#311)
Because we unregistered standard_error, if there
was an error booting livebook, we would fail to
print the error. This commit makes sure the original
standard error is added back whenever the proxy
process terminates.
2021-06-02 19:01:45 +02:00

134 lines
4 KiB
Elixir

defmodule Livebook.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
ensure_distribution!()
validate_hostname_resolution!()
set_cookie()
children = [
# Start the Telemetry supervisor
LivebookWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: Livebook.PubSub},
# Start the our own :standard_error handler (standard error -> group leader)
# This way we can run multiple embedded runtimes without worrying
# about restoring :standard_error to a valid process when terminating
{Livebook.Runtime.ErlDist.IOForwardGL, name: :standard_error},
# Start the supervisor dynamically managing sessions
Livebook.SessionSupervisor,
# Start the server responsible for associating files with sessions
Livebook.Session.FileGuard,
# Start the Node Pool for managing node names
Livebook.Runtime.NodePool,
# Start the Endpoint (http/https)
LivebookWeb.Endpoint
]
# Similarly as with :standard_error, we register our backend
# within the Livebook node, specifically for the embedded runtime
Logger.add_backend(Livebook.Runtime.ErlDist.LoggerGLBackend)
opts = [strategy: :one_for_one, name: Livebook.Supervisor]
with {:ok, _} = result <- Supervisor.start_link(children, opts) do
display_startup_info()
result
end
end
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
def config_change(changed, _new, removed) do
LivebookWeb.Endpoint.config_change(changed, removed)
:ok
end
defp ensure_distribution!() do
unless Node.alive?() do
case System.cmd("epmd", ["-daemon"]) do
{_, 0} ->
:ok
_ ->
Livebook.Config.abort!("""
could not start epmd (Erlang Port Mapper Driver). Livebook uses epmd to \
talk to different runtimes. You may have to start epmd explicitly by calling:
epmd -daemon
Or by calling:
elixir --sname test -e "IO.puts node()"
Then you can try booting Livebook again
""")
end
{type, name} = get_node_type_and_name()
case Node.start(name, type) do
{:ok, _} ->
:ok
{:error, reason} ->
Livebook.Config.abort!("could not start distributed node: #{inspect(reason)}")
end
end
end
# See https://github.com/elixir-nx/livebook/issues/302
defp validate_hostname_resolution!() do
unless Livebook.Config.longname() do
hostname = Livebook.Utils.node_host() |> to_charlist()
if :inet.gethostbyname(hostname) == {:error, :nxdomain} do
Livebook.Config.abort!("""
your hostname "#{hostname}" does not resolve to any IP address, which indicates something wrong in your OS configuration.
Make sure your computer's name resolves locally or start Livebook using a long distribution name. If you are using Livebook's CLI, you can:
livebook server --name livebook@127.0.0.1
If you are running it from source, do instead:
MIX_ENV=prod elixir --name livebook@127.0.0.1 -S mix phx.server
""")
end
end
end
defp set_cookie() do
cookie = Application.fetch_env!(:livebook, :cookie)
Node.set_cookie(cookie)
end
defp get_node_type_and_name() do
Application.get_env(:livebook, :node) || {:shortnames, random_short_name()}
end
defp random_short_name() do
:"livebook_#{Livebook.Utils.random_short_id()}"
end
defp display_startup_info() do
if Phoenix.Endpoint.server?(:livebook, LivebookWeb.Endpoint) do
IO.puts("[Livebook] Application running at #{access_url()}")
end
end
defp access_url() do
root_url = LivebookWeb.Endpoint.url()
if Livebook.Config.auth_mode() == :token do
token = Application.fetch_env!(:livebook, :token)
root_url <> "/?token=" <> token
else
root_url
end
end
end