Always run distribution in long names mode (#2646)

This commit is contained in:
Jonatan Kłosko 2024-06-13 10:06:08 +02:00 committed by GitHub
parent a3baf2aa89
commit ba4e59f416
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 60 additions and 192 deletions

View file

@ -220,10 +220,6 @@ The following environment variables can be used to configure Livebook on boot:
"standalone" (Elixir standalone), "attached:NODE:COOKIE" (Attached node)
or "embedded" (Embedded). Defaults to "standalone".
* `LIVEBOOK_DISTRIBUTION` - sets the node distribution for running Livebook in a
cluster. Must be "name" (long names) or "sname" (short names). Note that this
sets RELEASE_DISTRIBUTION if present when creating a release. Defaults to "sname".
* `LIVEBOOK_EPMDLESS` - if set to "true", it disables the usage of EPMD. This is
only supported within releases and defaults to true for the Desktop app.
@ -308,9 +304,6 @@ such as:
* [the `PATH` environment variable](https://en.wikipedia.org/wiki/PATH_(variable))
* set `LIVEBOOK_DISTRIBUTION=name` to enable notebooks to communicate
with nodes in other machines
* or to configure the Erlang VM, for instance, by setting
`ERL_AFLAGS="-proto_dist inet6_tcp"` if you need Livebook to run over IPv6

View file

@ -28,9 +28,3 @@ config :livebook,
agent_name: "chonky-cat"
config :livebook, Livebook.Apps.Manager, retry_backoff_base_ms: 0
# Use longnames when running tests in CI, so that no host resolution is required,
# see https://github.com/livebook-dev/livebook/pull/173#issuecomment-819468549
if System.get_env("CI") == "true" do
config :livebook, :node, {:longnames, :"livebook@127.0.0.1"}
end

View file

@ -1,6 +1,6 @@
# Clustering
If you plan to run several Livebook instances behind a load balancer, you need to enable clustering via the `LIVEBOOK_CLUSTER` environment variable in your Docker image. `LIVEBOOK_DISTRIBUTION` is automatically set to `name` if clustering is enabled.
If you plan to run several Livebook instances behind a load balancer, you need to enable clustering via the `LIVEBOOK_CLUSTER` environment variable in your Docker image.
Depending on the clustering strategy of your choice, you must set additional environment variables, oftentimes, at runtime. When using the Livebook Docker image, you can create a file at `/app/user/env.sh` that exports the necessary environment variables. This file is invoked right before booting Livebook.

View file

@ -44,7 +44,7 @@ system you want to connect to. For example, to connect to a
you may start it as follows:
```shell
$ iex --sname phoenix-app --cookie secret -S mix phx.server
$ iex --name phoenix-app@127.0.0.1 --cookie secret -S mix phx.server
```
With this information in hand, you can query and automate tasks within

View file

@ -199,9 +199,7 @@ defmodule Livebook do
config :livebook, :cacertfile, cacertfile
end
if rewrite_on = Livebook.Config.rewrite_on!("LIVEBOOK_PROXY_HEADERS") do
config :livebook, :rewrite_on, rewrite_on
end
config :livebook, :rewrite_on, Livebook.Config.rewrite_on!("LIVEBOOK_PROXY_HEADERS")
config :livebook,
:cookie,
@ -209,7 +207,15 @@ defmodule Livebook do
Livebook.Config.cookie!("RELEASE_COOKIE") ||
Livebook.Utils.random_cookie()
if node = Livebook.Config.node!("LIVEBOOK_NODE", "LIVEBOOK_DISTRIBUTION") do
# TODO: remove in v1.0
if System.get_env("LIVEBOOK_DISTRIBUTION") == "sname" do
IO.warn(
~s/Ignoring LIVEBOOK_DISTRIBUTION=sname, because short names are no longer supported./,
[]
)
end
if node = Livebook.Config.node!("LIVEBOOK_NODE") do
config :livebook, :node, node
end

View file

@ -13,7 +13,6 @@ defmodule Livebook.Application do
else
ensure_epmd!()
ensure_distribution!()
validate_hostname_resolution!()
end
set_cookie()
@ -164,9 +163,9 @@ defmodule Livebook.Application do
defp ensure_distribution!() do
unless Node.alive?() do
{type, name} = get_node_type_and_name()
node = get_node_name()
case Node.start(name, type) do
case Node.start(node, :longnames) do
{:ok, _} ->
:ok
@ -179,88 +178,24 @@ defmodule Livebook.Application do
import Record
defrecordp :hostent, Record.extract(:hostent, from_lib: "kernel/include/inet.hrl")
# See https://github.com/livebook-dev/livebook/issues/302
defp validate_hostname_resolution!() do
unless Livebook.Config.longname() do
[nodename, hostname] = node() |> Atom.to_charlist() |> :string.split(~c"@")
# erl_epmd names do not support ipv6 resolution by default,
# unless inet6 is configured, so we attempt both.
gethostbyname =
with {:error, _} <- :inet.gethostbyname(hostname, :inet, :infinity),
{:error, _} <- :inet.gethostbyname(hostname, :inet6, :infinity),
do: :error
with {:ok, hostent(h_addr_list: [epmd_addr | _])} <- gethostbyname,
{:ok, nodenames} <- :erl_epmd.names(epmd_addr),
true <- List.keymember?(nodenames, nodename, 0) do
:ok
else
_ ->
hint =
cond do
is_nil(System.get_env("LIVEBOOK_DESKTOP")) ->
"""
* If you are using Livebook's CLI or from source, consider using longnames:
livebook server --name livebook@127.0.0.1
elixir --name livebook@127.0.0.1 -S mix phx.server
"""
match?({:win32, _}, :os.type()) ->
path =
Path.join(
System.get_env("USERPROFILE", "%USERPROFILE%"),
".livebookdesktop.bat"
)
"""
* Configure your Livebook Desktop to use long names by creating a file at #{path} with:
set LIVEBOOK_DISTRIBUTION=name
set LIVEBOOK_NODE=livebook@127.0.0.1
"""
true ->
path = Path.join(System.get_env("HOME", "~"), ".livebookdesktop.sh")
"""
* Configure your Livebook Desktop to use long names by creating a file at #{path} with:
export LIVEBOOK_DISTRIBUTION=name
export LIVEBOOK_NODE=livebook@127.0.0.1
"""
end
Livebook.Config.abort!("""
Your hostname \"#{hostname}\" does not resolve to a loopback address (127.0.0.0/8), \
which indicates something wrong in your OS configuration, or EPMD is not running.
To address this issue, you might:
* Consult our Installation FAQ:
https://github.com/livebook-dev/livebook/wiki/Installation-FAQ
#{hint}\
* If the issue persists, please file a bug report
""")
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()}
defp get_node_name() do
Application.get_env(:livebook, :node) || random_long_name()
end
defp random_short_name() do
:"livebook_#{Livebook.Utils.random_short_id()}"
defp random_long_name() do
host =
if Livebook.Utils.proto_dist() == :inet6_tcp do
"::1"
else
"127.0.0.1"
end
:"livebook_#{Livebook.Utils.random_short_id()}@#{host}"
end
defp display_startup_info() do

View file

@ -64,18 +64,6 @@ defmodule Livebook.Config do
]
end
@doc """
Returns the longname if the distribution mode is configured to use long names.
"""
@spec longname() :: binary() | nil
def longname() do
host = Livebook.Utils.node_host()
if host =~ "." do
host
end
end
@doc """
Returns the default runtime.
"""
@ -532,21 +520,11 @@ defmodule Livebook.Config do
end
@doc """
Parses node and distribution type from env.
Parses node from env.
"""
def node!(node_env, distribution_env) do
case {System.get_env(node_env), System.get_env(distribution_env, "sname")} do
{nil, _} ->
nil
{name, "name"} ->
{:longnames, String.to_atom(name)}
{sname, "sname"} ->
{:shortnames, String.to_atom(sname)}
{_, other} ->
abort!(~s(#{distribution_env} must be one of "name" or "sname", got "#{other}"))
def node!(env) do
if node = System.get_env(env) do
String.to_atom(node)
end
end

View file

@ -140,8 +140,8 @@ defmodule Livebook.Runtime.ElixirStandalone do
# Also note that we explicitly halt, just in case `System.no_halt(true)` is
# called within the runtime.
@child_node_eval_string """
{:ok, [[mode, node]]} = :init.get_argument(:livebook_current);\
{:ok, _} = :net_kernel.start(List.to_atom(node), %{name_domain: List.to_atom(mode)});\
{:ok, [[node]]} = :init.get_argument(:livebook_current);\
{:ok, _} = :net_kernel.start(List.to_atom(node), %{name_domain: :longnames});\
{:ok, [[parent_node, _port]]} = :init.get_argument(:livebook_parent);\
dist_port = :persistent_term.get(:livebook_dist_port, 0);\
init_ref = make_ref();\
@ -164,8 +164,6 @@ defmodule Livebook.Runtime.ElixirStandalone do
parent_name = node()
parent_port = Livebook.EPMD.dist_port()
mode = if Livebook.Config.longname(), do: :longnames, else: :shortnames
epmdless_flags =
if parent_port != 0 do
"-epmd_module Elixir.Livebook.EPMD -start_epmd false -erl_epmd_port 0 "
@ -181,8 +179,9 @@ defmodule Livebook.Runtime.ElixirStandalone do
# Enable ANSI escape codes as we handle them with HTML.
# Disable stdin, so that the system process never tries to read terminal input.
"+sbwt none +sbwtdcpu none +sbwtdio none +sssdio 128 -elixir ansi_enabled true -noinput " <>
"-proto_dist #{Livebook.Utils.proto_dist()} " <>
epmdless_flags <>
"-livebook_parent #{parent_name} #{parent_port} -livebook_current #{mode} #{node_name}",
"-livebook_parent #{parent_name} #{parent_port} -livebook_current #{node_name}",
# Add the location of Livebook.EPMD
"-pa",
Application.app_dir(:livebook, "priv/epmd"),

View file

@ -133,6 +133,18 @@ defmodule Livebook.Utils do
host
end
@doc """
Returns the protocol for Erlang distribution used by the current node.
"""
@spec proto_dist() :: :inet_tcp | :inet6_tcp | :inet_tls
def proto_dist() do
case :init.get_argument(:proto_dist) do
{:ok, [[~c"inet6_tcp"]]} -> :inet6_tcp
{:ok, [[~c"inet_tls"]]} -> :inet_tls
_ -> :inet_tcp
end
end
@doc """
Registers the given process under `name` for the time of `fun` evaluation.
"""

View file

@ -44,7 +44,6 @@ defmodule LivebookCLI.Server do
Must be a valid IPv4 or IPv6 address
--name Sets a name for the app distributed node
-p, --port The port to start the web application on, defaults to 8080
--sname Sets a short name for the app distributed node
The --help option can be given to print this notice.
@ -195,8 +194,7 @@ defmodule LivebookCLI.Server do
ip: :string,
name: :string,
port: :integer,
home: :string,
sname: :string
home: :string
]
@aliases [
@ -204,15 +202,7 @@ defmodule LivebookCLI.Server do
]
defp args_to_options(args) do
{opts, extra_args} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)
validate_options!(opts)
{opts, extra_args}
end
defp validate_options!(opts) do
if Keyword.has_key?(opts, :name) and Keyword.has_key?(opts, :sname) do
raise "the provided --sname and --name options are mutually exclusive, please specify only one of them"
end
OptionParser.parse!(args, strict: @switches, aliases: @aliases)
end
defp opts_to_config([], config), do: config
@ -230,14 +220,9 @@ defmodule LivebookCLI.Server do
])
end
defp opts_to_config([{:sname, sname} | opts], config) do
sname = String.to_atom(sname)
opts_to_config(opts, [{:livebook, :node, {:shortnames, sname}} | config])
end
defp opts_to_config([{:name, name} | opts], config) do
name = String.to_atom(name)
opts_to_config(opts, [{:livebook, :node, {:longnames, name}} | config])
opts_to_config(opts, [{:livebook, :node, name} | config])
end
defp opts_to_config([{:cookie, cookie} | opts], config) do

View file

@ -60,11 +60,7 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
Make sure to give the node a name and a cookie, for example:
</p>
<div class="text-gray-700 markdown">
<%= if longname = Livebook.Config.longname() do %>
<pre><code>iex --name test@<%= longname %> --cookie mycookie -S mix</code></pre>
<% else %>
<pre><code>iex --sname test --cookie mycookie -S mix</code></pre>
<% end %>
<pre><code>iex --name <%= test_node() %> --cookie mycookie -S mix</code></pre>
</div>
<p class="text-gray-700">
Then enter the connection information below:
@ -79,7 +75,7 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
spellcheck="false"
>
<div class="flex flex-col space-y-4 mb-5">
<.text_field field={f[:name]} label="Name" placeholder={name_placeholder()} />
<.text_field field={f[:name]} label="Name" placeholder={test_node()} />
<.text_field field={f[:cookie]} label="Cookie" placeholder="mycookie" />
</div>
<.button type="submit" disabled={not @changeset.valid?}>
@ -138,7 +134,7 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
changeset.valid? and changeset.data == apply_changes(changeset)
end
defp name_placeholder do
if longname = Livebook.Config.longname(), do: "test@#{longname}", else: "test"
defp test_node() do
"test@#{Livebook.Utils.node_host()}"
end
end

View file

@ -13,8 +13,7 @@ set ELIXIR_ERL_OPTIONS=!ELIXIR_ERL_OPTIONS! -epmd_module Elixir.Livebook.EPMD -s
:continue
for /f "skip=1" %%X in ('wmic os get localdatetime') do if not defined TIMESTAMP set TIMESTAMP=%%X
if defined LIVEBOOK_DISTRIBUTION set RELEASE_DISTRIBUTION=!LIVEBOOK_DISTRIBUTION!
if not defined RELEASE_DISTRIBUTION set RELEASE_DISTRIBUTION=sname
set RELEASE_DISTRIBUTION="name"
if defined LIVEBOOK_NODE set RELEASE_NODE=!LIVEBOOK_NODE!
if not defined RELEASE_NODE set RELEASE_NODE=livebook-app-!TIMESTAMP:~8,6!-!RANDOM!

View file

@ -2,7 +2,7 @@ if [ -f "$HOME/.livebookdesktop.sh" ]; then
. "$HOME/.livebookdesktop.sh"
fi
export RELEASE_DISTRIBUTION=${LIVEBOOK_DISTRIBUTION:-${RELEASE_DISTRIBUTION:-"sname"}}
export RELEASE_DISTRIBUTION="name"
export RELEASE_NODE=${LIVEBOOK_NODE:-${RELEASE_NODE:-"livebook-app-$(cat /dev/urandom | env LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)"}}
export RELEASE_MODE=interactive
export MIX_ARCHIVES="${RELEASE_ROOT}/vendor/archives"

View file

@ -152,7 +152,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
# See https://hexdocs.pm/livebook/readme.html#environment-variables for all available environment variables.
# # Allow Livebook to connect to remote machines over IPv6
# export LIVEBOOK_DISTRIBUTION=name
# export ERL_AFLAGS="-proto_dist inet6_tcp"
# # Add Homebrew to PATH

View file

@ -168,7 +168,6 @@ rem If you change this file, you must restart Livebook for your changes to take
rem See https://hexdocs.pm/livebook/readme.html#environment-variables for all available enviornment variables.
rem Allow Livebook to connect to remote machines over IPv6
rem set LIVEBOOK_DISTRIBUTION=name
rem set ERL_AFLAGS=-proto_dist inet6_tcp
rem Add directory to PATH

View file

@ -13,8 +13,7 @@ set ELIXIR_ERL_OPTIONS=!ELIXIR_ERL_OPTIONS! -epmd_module Elixir.Livebook.EPMD -s
set RELEASE_MODE=interactive
if defined LIVEBOOK_NODE set RELEASE_NODE="!LIVEBOOK_NODE!"
if not defined RELEASE_NODE set RELEASE_NODE=livebook_server
if defined LIVEBOOK_DISTRIBUTION set RELEASE_DISTRIBUTION="!LIVEBOOK_DISTRIBUTION!"
if not defined RELEASE_DISTRIBUTION (if defined LIVEBOOK_CLUSTER set RELEASE_DISTRIBUTION="name")
set RELEASE_DISTRIBUTION="name"
if defined LIVEBOOK_COOKIE set RELEASE_COOKIE="!LIVEBOOK_COOKIE!"
set cookie_path="!RELEASE_ROOT!\releases\COOKIE"

View file

@ -1,10 +1,5 @@
DISTRIBUTION_DEFAULT="sname"
NODE_DEFAULT="livebook_server"
if [ ! -z "$LIVEBOOK_CLUSTER" ]; then
DISTRIBUTION_DEFAULT="name";
fi
if [ "$LIVEBOOK_CLUSTER" = "fly" ]; then
export ERL_AFLAGS="-proto_dist inet6_tcp"
export LIVEBOOK_CLUSTER="dns:${FLY_APP_NAME}.internal"
@ -17,7 +12,7 @@ fi
export RELEASE_MODE=interactive
export RELEASE_NODE=${LIVEBOOK_NODE:-${RELEASE_NODE:-${NODE_DEFAULT}}}
export RELEASE_DISTRIBUTION=${LIVEBOOK_DISTRIBUTION:-${RELEASE_DISTRIBUTION:-${DISTRIBUTION_DEFAULT}}}
export RELEASE_DISTRIBUTION="name"
if [ "$LIVEBOOK_EPMDLESS" = "true" ] || [ "$LIVEBOOK_EPMDLESS" = "1" ]; then
export ELIXIR_ERL_OPTIONS="${ELIXIR_ERL_OPTIONS} -epmd_module Elixir.Livebook.EPMD -start_epmd false -erl_epmd_port 0"

View file

@ -13,30 +13,9 @@ defmodule Livebook.ConfigTest do
end
describe "node!/1" do
test "parses longnames" do
with_env([TEST_LIVEBOOK_NODE: "test@::1", TEST_LIVEBOOK_DISTRIBUTION: "name"], fn ->
assert Config.node!("TEST_LIVEBOOK_NODE", "TEST_LIVEBOOK_DISTRIBUTION") ==
{:longnames, :"test@::1"}
end)
end
test "parses shortnames" do
with_env([TEST_LIVEBOOK_NODE: "test", TEST_LIVEBOOK_DISTRIBUTION: "sname"], fn ->
assert Config.node!("TEST_LIVEBOOK_NODE", "TEST_LIVEBOOK_DISTRIBUTION") ==
{:shortnames, :test}
end)
end
test "parses shortnames by default" do
with_env([TEST_LIVEBOOK_NODE: "test", TEST_LIVEBOOK_DISTRIBUTION: nil], fn ->
assert Config.node!("TEST_LIVEBOOK_NODE", "TEST_LIVEBOOK_DISTRIBUTION") ==
{:shortnames, :test}
end)
end
test "returns nil if node is not set" do
with_env([TEST_LIVEBOOK_NODE: nil, TEST_LIVEBOOK_DISTRIBUTION: "name"], fn ->
assert Config.node!("TEST_LIVEBOOK_NODE", "TEST_LIVEBOOK_DISTRIBUTION") == nil
test "parses node" do
with_env([TEST_LIVEBOOK_NODE: "test@::1"], fn ->
assert Config.node!("TEST_LIVEBOOK_NODE") == :"test@::1"
end)
end
end

View file

@ -164,7 +164,7 @@ defmodule Livebook.TeamsServer do
args = [
"-e",
"spawn(fn -> IO.gets([]) && System.halt(0) end)",
"--sname",
"--name",
to_string(state.node),
"--cookie",
to_string(Node.get_cookie()),