Merge branch 'master' into rabbitmq-cli-424

This commit is contained in:
Michael Klishin 2020-06-05 17:22:08 +03:00 committed by GitHub
commit 0e577ee358
2 changed files with 179 additions and 56 deletions

View File

@ -299,8 +299,8 @@ context_to_app_env_vars1(
end, end,
ok. ok.
context_to_code_path(#{plugins_path := PluginsPath}) -> context_to_code_path(#{os_type := OSType, plugins_path := PluginsPath}) ->
Dirs = get_user_lib_dirs(PluginsPath), Dirs = get_user_lib_dirs(OSType, PluginsPath),
code:add_pathsa(lists:reverse(Dirs)). code:add_pathsa(lists:reverse(Dirs)).
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
@ -309,8 +309,8 @@ context_to_code_path(#{plugins_path := PluginsPath}) ->
%% The goal is to mimic the behavior of the `$ERL_LIBS` environment %% The goal is to mimic the behavior of the `$ERL_LIBS` environment
%% variable. %% variable.
get_user_lib_dirs(Path) -> get_user_lib_dirs(OSType, Path) ->
Sep = case os:type() of Sep = case OSType of
{win32, _} -> ";"; {win32, _} -> ";";
_ -> ":" _ -> ":"
end, end,
@ -1426,11 +1426,28 @@ load_conf_env_file(#{os_type := {win32, _},
Context1 = update_context(Context, conf_env_file, ConfEnvFile, Origin), Context1 = update_context(Context, conf_env_file, ConfEnvFile, Origin),
case loading_conf_env_file_enabled(Context1) of case loading_conf_env_file_enabled(Context1) of
true -> true ->
rabbit_log_prelaunch:notice( case filelib:is_regular(ConfEnvFile) of
"Loading of $RABBITMQ_CONF_ENV_FILE (~s) " false ->
"is not implemented for Windows", rabbit_log_prelaunch:debug(
[ConfEnvFile]), "No $RABBITMQ_CONF_ENV_FILE (~ts)", [ConfEnvFile]),
Context1; Context1;
true ->
case os:find_executable("cmd.exe") of
false ->
Cmd = os:getenv("ComSpec"),
CmdExists =
Cmd =/= false andalso
filelib:is_regular(Cmd),
case CmdExists of
false -> Context1;
true -> do_load_conf_env_file(Context1,
Cmd,
ConfEnvFile)
end;
Cmd ->
do_load_conf_env_file(Context1, Cmd, ConfEnvFile)
end
end;
false -> false ->
rabbit_log_prelaunch:debug( rabbit_log_prelaunch:debug(
"Loading of $RABBITMQ_CONF_ENV_FILE (~ts) is disabled", "Loading of $RABBITMQ_CONF_ENV_FILE (~ts) is disabled",
@ -1455,22 +1472,28 @@ loading_conf_env_file_enabled(_) ->
erlang:get({?MODULE, always_undefined}) =:= undefined. erlang:get({?MODULE, always_undefined}) =:= undefined.
-endif. -endif.
do_load_conf_env_file(Context, Sh, ConfEnvFile) -> do_load_conf_env_file(#{os_type := {unix, _}} = Context, Sh, ConfEnvFile) ->
rabbit_log_prelaunch:debug( rabbit_log_prelaunch:debug(
"Sourcing $RABBITMQ_CONF_ENV_FILE: ~ts", [ConfEnvFile]), "Sourcing $RABBITMQ_CONF_ENV_FILE: ~ts", [ConfEnvFile]),
Marker = rabbit_misc:format(
"-----BEGIN VARS LIST FOR PID ~s-----", [os:getpid()]), %% The script below sources the `CONF_ENV_FILE` file, then it shows a
%% marker line and all environment variables.
%%
%% The marker line is useful to distinguish any output from the sourced
%% script from the variables we are interested in.
Marker = vars_list_marker(),
Script = rabbit_misc:format( Script = rabbit_misc:format(
". \"~ts\" && " ". \"~ts\" && "
"echo \"~s\" && " "echo \"~s\" && "
"set", [ConfEnvFile, Marker]), "set", [ConfEnvFile, Marker]),
Args = ["-ex", "-c", Script],
#{sys_prefix := SysPrefix, #{sys_prefix := SysPrefix,
rabbitmq_home := RabbitmqHome} = Context, rabbitmq_home := RabbitmqHome} = Context,
MainConfigFile = re:replace( MainConfigFile = re:replace(
get_default_main_config_file(Context), get_default_main_config_file(Context),
"\\.(conf|config)$", "", [{return, list}]), "\\.(conf|config)$", "", [{return, list}]),
%% The variables below are those the `CONF_ENV_FILE` file can expect.
Env = [ Env = [
{"SYS_PREFIX", SysPrefix}, {"SYS_PREFIX", SysPrefix},
{"RABBITMQ_HOME", RabbitmqHome}, {"RABBITMQ_HOME", RabbitmqHome},
@ -1482,34 +1505,92 @@ do_load_conf_env_file(Context, Sh, ConfEnvFile) ->
{"CONF_ENV_FILE_PHASE", "rabbtimq-prelaunch"} {"CONF_ENV_FILE_PHASE", "rabbtimq-prelaunch"}
], ],
Port = erlang:open_port( Args = ["-ex", "-c", Script],
{spawn_executable, Sh}, Opts = [{args, Args},
[{args, Args},
{env, Env}, {env, Env},
binary, binary,
use_stdio, use_stdio,
stderr_to_stdout, stderr_to_stdout,
exit_status]), exit_status],
collect_sh_output(Context, Port, Marker, <<>>). Port = erlang:open_port({spawn_executable, Sh}, Opts),
collect_conf_env_file_output(Context, Port, Marker, <<>>);
do_load_conf_env_file(#{os_type := {win32, _}} = Context, Cmd, ConfEnvFile) ->
%% rabbitmq/rabbitmq-common#392
rabbit_log_prelaunch:debug(
"Executing $RABBITMQ_CONF_ENV_FILE: ~ts", [ConfEnvFile]),
collect_sh_output(Context, Port, Marker, Output) -> %% The script below executes the `CONF_ENV_FILE` file, then it shows a
%% marker line and all environment variables.
%%
%% The marker line is useful to distinguish any output from the sourced
%% script from the variables we are interested in.
%%
%% Arguments are split into a list of strings to support a filename with
%% whitespaces in the path.
Marker = vars_list_marker(),
Script = [ConfEnvFile, "&&",
"echo", Marker, "&&",
"set"],
#{rabbitmq_base := RabbitmqBase,
rabbitmq_home := RabbitmqHome} = Context,
MainConfigFile = re:replace(
get_default_main_config_file(Context),
"\\.(conf|config)$", "", [{return, list}]),
%% The variables below are those the `CONF_ENV_FILE` file can expect.
Env = [
{"RABBITMQ_BASE", RabbitmqBase},
{"RABBITMQ_HOME", RabbitmqHome},
{"CONFIG_FILE", MainConfigFile},
{"ADVANCED_CONFIG_FILE", get_default_advanced_config_file(Context)},
{"MNESIA_BASE", get_default_mnesia_base_dir(Context)},
{"ENABLED_PLUGINS_FILE", get_default_enabled_plugins_file(Context)},
{"PLUGINS_DIR", get_default_plugins_path_from_env(Context)},
{"CONF_ENV_FILE_PHASE", "rabbtimq-prelaunch"}
],
Args = ["/Q", "/C" | Script],
Opts = [{args, Args},
{env, Env},
hide,
binary,
stderr_to_stdout,
exit_status],
Port = erlang:open_port({spawn_executable, Cmd}, Opts),
collect_conf_env_file_output(Context, Port, "\"" ++ Marker ++ "\" ", <<>>).
vars_list_marker() ->
rabbit_misc:format(
"-----BEGIN VARS LIST FOR PID ~s-----", [os:getpid()]).
collect_conf_env_file_output(Context, Port, Marker, Output) ->
receive receive
{Port, {exit_status, ExitStatus}} -> {Port, {exit_status, ExitStatus}} ->
rabbit_log_prelaunch:debug( Lines = post_port_cmd_output(Context, Output, ExitStatus),
"$RABBITMQ_CONF_ENV_FILE exit status: ~b", [ExitStatus]),
DecodedOutput = unicode:characters_to_list(Output),
Lines = string:split(string:trim(DecodedOutput), "\n", all),
rabbit_log_prelaunch:debug("$RABBITMQ_CONF_ENV_FILE output:"),
[rabbit_log_prelaunch:debug(" ~ts", [Line])
|| Line <- Lines],
case ExitStatus of case ExitStatus of
0 -> parse_conf_env_file_output(Context, Marker, Lines); 0 -> parse_conf_env_file_output(Context, Marker, Lines);
_ -> Context _ -> Context
end; end;
{Port, {data, Chunk}} -> {Port, {data, Chunk}} ->
collect_sh_output(Context, Port, Marker, [Output, Chunk]) collect_conf_env_file_output(
Context, Port, Marker, [Output, Chunk])
end. end.
post_port_cmd_output(#{os_type := {OSType, _}}, Output, ExitStatus) ->
rabbit_log_prelaunch:debug(
"$RABBITMQ_CONF_ENV_FILE exit status: ~b",
[ExitStatus]),
DecodedOutput = unicode:characters_to_list(Output),
LineSep = case OSType of
win32 -> "\r\n";
_ -> "\n"
end,
Lines = string:split(string:trim(DecodedOutput), LineSep, all),
rabbit_log_prelaunch:debug("$RABBITMQ_CONF_ENV_FILE output:"),
[rabbit_log_prelaunch:debug(" ~ts", [Line]) || Line <- Lines],
Lines.
parse_conf_env_file_output(Context, _, []) -> parse_conf_env_file_output(Context, _, []) ->
Context; Context;
parse_conf_env_file_output(Context, Marker, [Marker | Lines]) -> parse_conf_env_file_output(Context, Marker, [Marker | Lines]) ->

View File

@ -22,6 +22,8 @@
-export([all/0, -export([all/0,
suite/0, suite/0,
groups/0, groups/0,
init_per_suite/1,
end_per_suite/1,
init_per_group/2, init_per_group/2,
end_per_group/2, end_per_group/2,
init_per_testcase/2, init_per_testcase/2,
@ -110,6 +112,14 @@ groups() ->
{parallel_tests, [parallel], all()} {parallel_tests, [parallel], all()}
]. ].
init_per_suite(Config) ->
persistent_term:put({rabbit_env, load_conf_env_file}, false),
Config.
end_per_suite(Config) ->
persistent_term:erase({rabbit_env, load_conf_env_file}),
Config.
init_per_group(_, Config) -> Config. init_per_group(_, Config) -> Config.
end_per_group(_, Config) -> Config. end_per_group(_, Config) -> Config.
@ -138,7 +148,6 @@ check_data_dir(_) ->
check_default_values(_) -> check_default_values(_) ->
%% When `rabbit_env` is built with `TEST` defined, we can override %% When `rabbit_env` is built with `TEST` defined, we can override
%% the OS type. %% the OS type.
persistent_term:put({rabbit_env, load_conf_env_file}, false),
persistent_term:put({rabbit_env, os_type}, {unix, undefined}), persistent_term:put({rabbit_env, os_type}, {unix, undefined}),
UnixContext = rabbit_env:get_context(), UnixContext = rabbit_env:get_context(),
@ -152,7 +161,6 @@ check_default_values(_) ->
end, end,
persistent_term:erase({rabbit_env, os_type}), persistent_term:erase({rabbit_env, os_type}),
persistent_term:erase({rabbit_env, load_conf_env_file}),
{RFFValue, RFFOrigin} = forced_feature_flags_on_init_expect(), {RFFValue, RFFOrigin} = forced_feature_flags_on_init_expect(),
@ -359,12 +367,10 @@ check_values_from_reachable_remote_node(Config) ->
wait_for_remote_node(Node), wait_for_remote_node(Node),
try try
persistent_term:put({rabbit_env, load_conf_env_file}, false),
persistent_term:put({rabbit_env, os_type}, {unix, undefined}), persistent_term:put({rabbit_env, os_type}, {unix, undefined}),
UnixContext = rabbit_env:get_context(Node), UnixContext = rabbit_env:get_context(Node),
persistent_term:erase({rabbit_env, os_type}), persistent_term:erase({rabbit_env, os_type}),
persistent_term:erase({rabbit_env, load_conf_env_file}),
{RFFValue, RFFOrigin} = forced_feature_flags_on_init_expect(), {RFFValue, RFFOrigin} = forced_feature_flags_on_init_expect(),
@ -475,12 +481,10 @@ check_values_from_offline_remote_node(_) ->
NodeS = atom_to_list(Node), NodeS = atom_to_list(Node),
true = os:putenv("RABBITMQ_NODENAME", NodeS), true = os:putenv("RABBITMQ_NODENAME", NodeS),
persistent_term:put({rabbit_env, load_conf_env_file}, false),
persistent_term:put({rabbit_env, os_type}, {unix, undefined}), persistent_term:put({rabbit_env, os_type}, {unix, undefined}),
UnixContext = rabbit_env:get_context(offline), UnixContext = rabbit_env:get_context(offline),
persistent_term:erase({rabbit_env, os_type}), persistent_term:erase({rabbit_env, os_type}),
persistent_term:erase({rabbit_env, load_conf_env_file}),
os:unsetenv("RABBITMQ_NODENAME"), os:unsetenv("RABBITMQ_NODENAME"),
{RFFValue, RFFOrigin} = forced_feature_flags_on_init_expect(), {RFFValue, RFFOrigin} = forced_feature_flags_on_init_expect(),
@ -568,12 +572,10 @@ check_values_from_offline_remote_node(_) ->
check_context_to_app_env_vars(_) -> check_context_to_app_env_vars(_) ->
%% When `rabbit_env` is built with `TEST` defined, we can override %% When `rabbit_env` is built with `TEST` defined, we can override
%% the OS type. %% the OS type.
persistent_term:put({rabbit_env, load_conf_env_file}, false),
persistent_term:put({rabbit_env, os_type}, {unix, undefined}), persistent_term:put({rabbit_env, os_type}, {unix, undefined}),
UnixContext = rabbit_env:get_context(), UnixContext = rabbit_env:get_context(),
persistent_term:erase({rabbit_env, os_type}), persistent_term:erase({rabbit_env, os_type}),
persistent_term:erase({rabbit_env, load_conf_env_file}),
Vars = [{mnesia, dir, maps:get(mnesia_dir, UnixContext)}, Vars = [{mnesia, dir, maps:get(mnesia_dir, UnixContext)},
{ra, data_dir, maps:get(quorum_queue_dir, UnixContext)}, {ra, data_dir, maps:get(quorum_queue_dir, UnixContext)},
@ -630,36 +632,76 @@ check_context_to_code_path(Config) ->
ok = file:make_dir(MyPlugin2Dir), ok = file:make_dir(MyPlugin2Dir),
ok = file:make_dir(MyPlugin2EbinDir), ok = file:make_dir(MyPlugin2EbinDir),
%% When `rabbit_env` is built with `TEST` defined, we can override %% On Unix.
%% the OS type. %%
PluginsPath = PluginsDir1 ++ ":" ++ PluginsDir2, %% We can't test the Unix codepath on Windows because the drive letter
true = os:putenv("RABBITMQ_PLUGINS_DIR", PluginsPath), %% separator conflicts with the path separator (they are both ':').
persistent_term:put({rabbit_env, load_conf_env_file}, false), %% However, the Windows codepath can be tested on both Unix and Windows.
case os:type() of
{unix, _} ->
UnixPluginsPath = PluginsDir1 ++ ":" ++ PluginsDir2,
true = os:putenv("RABBITMQ_PLUGINS_DIR", UnixPluginsPath),
persistent_term:put({rabbit_env, os_type}, {unix, undefined}), persistent_term:put({rabbit_env, os_type}, {unix, undefined}),
UnixContext = rabbit_env:get_context(), UnixContext = rabbit_env:get_context(),
persistent_term:erase({rabbit_env, os_type}), persistent_term:erase({rabbit_env, os_type}),
persistent_term:erase({rabbit_env, load_conf_env_file}),
os:unsetenv("RABBITMQ_PLUGINS_DIR"), os:unsetenv("RABBITMQ_PLUGINS_DIR"),
?assertEqual(PluginsPath, maps:get(plugins_path, UnixContext)), ?assertEqual(UnixPluginsPath, maps:get(plugins_path, UnixContext)),
OldCodePath = code:get_path(), OldCodePath1 = code:get_path(),
?assertNot(lists:member(MyPlugin1EbinDir, OldCodePath)), ?assertNot(lists:member(MyPlugin1EbinDir, OldCodePath1)),
?assertNot(lists:member(MyPlugin2EbinDir, OldCodePath)), ?assertNot(lists:member(MyPlugin2EbinDir, OldCodePath1)),
rabbit_env:context_to_code_path(UnixContext), rabbit_env:context_to_code_path(UnixContext),
NewCodePath = code:get_path(), NewCodePath1 = code:get_path(),
?assert(lists:member(MyPlugin1EbinDir, NewCodePath)), ?assert(lists:member(MyPlugin1EbinDir, NewCodePath1)),
?assert(lists:member(MyPlugin2EbinDir, NewCodePath)), ?assert(lists:member(MyPlugin2EbinDir, NewCodePath1)),
?assertEqual( ?assertEqual(
[MyPlugin1EbinDir, MyPlugin2EbinDir], [MyPlugin1EbinDir, MyPlugin2EbinDir],
lists:filter( lists:filter(
fun(Dir) -> fun(Dir) ->
Dir =:= MyPlugin1EbinDir orelse Dir =:= MyPlugin1EbinDir orelse
Dir =:= MyPlugin2EbinDir Dir =:= MyPlugin2EbinDir
end, NewCodePath)). end, NewCodePath1)),
true = code:del_path(MyPlugin1EbinDir),
true = code:del_path(MyPlugin2EbinDir);
_ ->
ok
end,
%% On Windows.
Win32PluginsPath = PluginsDir1 ++ ";" ++ PluginsDir2,
true = os:putenv("RABBITMQ_PLUGINS_DIR", Win32PluginsPath),
persistent_term:put({rabbit_env, os_type}, {win32, undefined}),
Win32Context = rabbit_env:get_context(),
persistent_term:erase({rabbit_env, os_type}),
os:unsetenv("RABBITMQ_PLUGINS_DIR"),
?assertEqual(Win32PluginsPath, maps:get(plugins_path, Win32Context)),
OldCodePath2 = code:get_path(),
?assertNot(lists:member(MyPlugin1EbinDir, OldCodePath2)),
?assertNot(lists:member(MyPlugin2EbinDir, OldCodePath2)),
rabbit_env:context_to_code_path(Win32Context),
NewCodePath2 = code:get_path(),
?assert(lists:member(MyPlugin1EbinDir, NewCodePath2)),
?assert(lists:member(MyPlugin2EbinDir, NewCodePath2)),
?assertEqual(
[MyPlugin1EbinDir, MyPlugin2EbinDir],
lists:filter(
fun(Dir) ->
Dir =:= MyPlugin1EbinDir orelse
Dir =:= MyPlugin2EbinDir
end, NewCodePath2)),
true = code:del_path(MyPlugin1EbinDir),
true = code:del_path(MyPlugin2EbinDir).
check_RABBITMQ_ADVANCED_CONFIG_FILE(_) -> check_RABBITMQ_ADVANCED_CONFIG_FILE(_) ->
Value1 = random_string(), Value1 = random_string(),