Skip to content

Commit

Permalink
[1371] Add Custom Hostname and Domain Options (#1372)
Browse files Browse the repository at this point in the history
When using `longnames` in projects that don't
adhere to the host provided hostname and domain
it is useful to be able to override those via the
`erlang_ls.config` file.  The code in this commit
provides these options as well as cleans up
some related duplicated code.

This code also fixes a small bug that would leave
a trailing dot `.` if a domain is not defined.

Tests have been updated to reflect these changes and
new tests have been created to test the new options.
  • Loading branch information
zachlankton committed Sep 8, 2022
1 parent 55a3854 commit 4afd022
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 48 deletions.
26 changes: 25 additions & 1 deletion apps/els_core/src/els_config_runtime.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
%% Getters
-export([
get_node_name/0,
get_hostname/0,
get_domain/0,
get_otp_path/0,
get_start_cmd/0,
get_start_args/0,
Expand All @@ -20,6 +22,8 @@
-spec default_config() -> config().
default_config() ->
#{
"hostname" => default_hostname(),
"domain" => default_domain(),
"node_name" => default_node_name(),
"otp_path" => default_otp_path(),
"start_cmd" => default_start_cmd(),
Expand All @@ -31,6 +35,17 @@ get_node_name() ->
Value = maps:get("node_name", els_config:get(runtime), default_node_name()),
els_utils:compose_node_name(Value, get_name_type()).

-spec get_hostname() -> string().
get_hostname() ->
case els_config:get(runtime) of
undefined -> default_hostname();
Runtime -> maps:get("hostname", Runtime, default_hostname())
end.

-spec get_domain() -> string().
get_domain() ->
maps:get("domain", els_config:get(runtime), default_domain()).

-spec get_otp_path() -> string().
get_otp_path() ->
maps:get("otp_path", els_config:get(runtime), default_otp_path()).
Expand Down Expand Up @@ -65,12 +80,21 @@ get_cookie() ->
-spec default_node_name() -> string().
default_node_name() ->
RootUri = els_config:get(root_uri),
{ok, Hostname} = inet:gethostname(),
Hostname = get_hostname(),
NodeName = els_distribution_server:normalize_node_name(
filename:basename(els_uri:path(RootUri))
),
NodeName ++ "@" ++ Hostname.

-spec default_hostname() -> string().
default_hostname() ->
{ok, Hostname} = inet:gethostname(),
Hostname.

-spec default_domain() -> string().
default_domain() ->
proplists:get_value(domain, inet:get_rc(), "").

-spec default_otp_path() -> string().
default_otp_path() ->
filename:dirname(filename:dirname(code:root_dir())).
Expand Down
11 changes: 1 addition & 10 deletions apps/els_core/src/els_distribution_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
rpc_call/3,
rpc_call/4,
node_name/2,
node_name/3,
normalize_node_name/1
]).

Expand Down Expand Up @@ -223,15 +222,7 @@ node_name(Prefix, Name0) ->
Name = normalize_node_name(Name0),
Int = erlang:phash2(erlang:timestamp()),
Id = lists:flatten(io_lib:format("~s_~s_~p", [Prefix, Name, Int])),
{ok, HostName} = inet:gethostname(),
node_name(Id, HostName, els_config_runtime:get_name_type()).

-spec node_name(string(), string(), 'longnames' | 'shortnames') -> atom().
node_name(Id, HostName, shortnames) ->
list_to_atom(Id ++ "@" ++ HostName);
node_name(Id, HostName, longnames) ->
Domain = proplists:get_value(domain, inet:get_rc(), ""),
list_to_atom(Id ++ "@" ++ HostName ++ "." ++ Domain).
els_utils:compose_node_name(Id, els_config_runtime:get_name_type()).

-spec normalize_node_name(string() | binary()) -> string().
normalize_node_name(Name) ->
Expand Down
9 changes: 6 additions & 3 deletions apps/els_core/src/els_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -453,15 +453,18 @@ compose_node_name(Name, Type) ->
true ->
Name;
_ ->
{ok, HostName} = inet:gethostname(),
HostName = els_config_runtime:get_hostname(),
Name ++ [$@ | HostName]
end,
case Type of
shortnames ->
list_to_atom(NodeName);
longnames ->
Domain = proplists:get_value(domain, inet:get_rc(), ""),
list_to_atom(NodeName ++ "." ++ Domain)
Domain = els_config_runtime:get_domain(),
case Domain of
"" -> list_to_atom(NodeName);
_ -> list_to_atom(NodeName ++ "." ++ Domain)
end
end.

%% @doc Given an MFA or a FA, return a printable version of the
Expand Down
32 changes: 7 additions & 25 deletions apps/els_dap/src/els_dap_general_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1023,22 +1023,6 @@ safe_eval(ProjectNode, Debugged, Expression, Update) ->
end,
Return.

-spec check_project_node_name(binary(), boolean()) -> atom().
check_project_node_name(ProjectNode, false) ->
binary_to_atom(ProjectNode, utf8);
check_project_node_name(ProjectNode, true) ->
case binary:match(ProjectNode, <<"@">>) of
nomatch ->
{ok, HostName} = inet:gethostname(),
BinHostName = list_to_binary(HostName),
DomainStr = proplists:get_value(domain, inet:get_rc(), ""),
Domain = list_to_binary(DomainStr),
BinName = <<ProjectNode/binary, "@", BinHostName/binary, ".", Domain/binary>>,
binary_to_atom(BinName, utf8);
_ ->
binary_to_atom(ProjectNode, utf8)
end.

-spec start_distribution(map()) -> {ok, map()} | {error, any()}.
start_distribution(Params) ->
#{<<"cwd">> := Cwd} = Params,
Expand All @@ -1062,23 +1046,21 @@ start_distribution(Params) ->
<<"cookie">> := ConfCookie,
<<"use_long_names">> := UseLongNames
} = Config,
ConfProjectNode = check_project_node_name(RawProjectNode, UseLongNames),
?LOG_INFO("Configured Project Node Name: ~p", [ConfProjectNode]),
Cookie = binary_to_atom(ConfCookie, utf8),

NameType =
case UseLongNames of
true ->
longnames;
false ->
shortnames
end,

ConfProjectNode0 = binary_to_list(RawProjectNode),
ConfProjectNode = els_utils:compose_node_name(ConfProjectNode0, NameType),
?LOG_INFO("Configured Project Node Name: ~p", [ConfProjectNode]),
Cookie = binary_to_atom(ConfCookie, utf8),

%% start distribution
Prefix = <<"erlang_ls_dap">>,
Int = erlang:phash2(erlang:timestamp()),
Id = lists:flatten(io_lib:format("~s_~s_~p", [Prefix, Name, Int])),
{ok, HostName} = inet:gethostname(),
LocalNode = els_distribution_server:node_name(Id, HostName, NameType),
LocalNode = els_distribution_server:node_name(<<"erlang_ls_dap">>, Name),
case
els_distribution_server:start_distribution(
LocalNode,
Expand Down
54 changes: 45 additions & 9 deletions apps/els_lsp/test/els_diagnostics_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
compiler_telemetry/1,
code_path_extra_dirs/1,
use_long_names/1,
use_long_names_no_domain/1,
use_long_names_custom_hostname/1,
epp_with_nonexistent_macro/1,
code_reload/1,
code_reload_sticky_mod/1,
Expand Down Expand Up @@ -120,15 +122,20 @@ init_per_testcase(code_path_extra_dirs, Config) ->
els_mock_diagnostics:setup(),
els_test_utils:init_per_testcase(code_path_extra_dirs, Config);
init_per_testcase(use_long_names, Config) ->
meck:new(yamerl, [passthrough, no_link]),
Content =
<<"runtime:\n", " use_long_names: true\n", " cookie: mycookie\n",
" node_name: my_node\n", " domain: test.local">>,
init_long_names_config(Content, Config);
init_per_testcase(use_long_names_no_domain, Config) ->
Content =
<<"runtime:\n", " use_long_names: true\n", " cookie: mycookie\n",
" node_name: my_node\n">>,
meck:expect(yamerl, decode_file, 2, fun(_, Opts) ->
yamerl:decode(Content, Opts)
end),
els_mock_diagnostics:setup(),
els_test_utils:init_per_testcase(code_path_extra_dirs, Config);
init_long_names_config(Content, Config);
init_per_testcase(use_long_names_custom_hostname, Config) ->
Content =
<<"runtime:\n", " use_long_names: true\n", " cookie: mycookie\n",
" node_name: my_node\n", " hostname: 127.0.0.1">>,
init_long_names_config(Content, Config);
init_per_testcase(exclude_unused_includes = TestCase, Config) ->
els_mock_diagnostics:setup(),
NewConfig = els_test_utils:init_per_testcase(TestCase, Config),
Expand Down Expand Up @@ -224,7 +231,9 @@ end_per_testcase(TestCase, Config) when
ok;
end_per_testcase(TestCase, Config) when
TestCase =:= code_path_extra_dirs orelse
TestCase =:= use_long_names
TestCase =:= use_long_names orelse
TestCase =:= use_long_names_no_domain orelse
TestCase =:= use_long_names_custom_hostname
->
meck:unload(yamerl),
els_test_utils:end_per_testcase(code_path_extra_dirs, Config),
Expand Down Expand Up @@ -271,6 +280,15 @@ end_per_testcase(TestCase, Config) ->
els_mock_diagnostics:teardown(),
ok.

-spec init_long_names_config(binary(), config()) -> config().
init_long_names_config(Content, Config) ->
meck:new(yamerl, [passthrough, no_link]),
meck:expect(yamerl, decode_file, 2, fun(_, Opts) ->
yamerl:decode(Content, Opts)
end),
els_mock_diagnostics:setup(),
els_test_utils:init_per_testcase(code_path_extra_dirs, Config).

% RefactorErl

%%==============================================================================
Expand Down Expand Up @@ -626,12 +644,30 @@ code_path_extra_dirs(_Config) ->

-spec use_long_names(config()) -> ok.
use_long_names(_Config) ->
{ok, HostName} = inet:gethostname(),
HostName = els_config_runtime:get_hostname(),
NodeName =
"my_node@" ++
HostName ++ "." ++
proplists:get_value(domain, inet:get_rc(), ""),
els_config_runtime:get_domain(),
Node = list_to_atom(NodeName),
?assertMatch(Node, els_config_runtime:get_node_name()),
ok.

-spec use_long_names_no_domain(config()) -> ok.
use_long_names_no_domain(_Config) ->
HostName = els_config_runtime:get_hostname(),
NodeName =
"my_node@" ++ HostName,
Node = list_to_atom(NodeName),
?assertMatch(Node, els_config_runtime:get_node_name()),
ok.

-spec use_long_names_custom_hostname(config()) -> ok.
use_long_names_custom_hostname(_Config) ->
HostName = els_config_runtime:get_hostname(),
NodeName = "[email protected]",
Node = list_to_atom(NodeName),
?assertMatch(HostName, "127.0.0.1"),
?assertMatch(Node, els_config_runtime:get_node_name()),
ok.

Expand Down

0 comments on commit 4afd022

Please sign in to comment.