rabbit_ct_helpers: Use arbitrary node name and TCP ports
Furthermore, if the node fails to start, try again with another name and set of TCP ports until we find a setup which boots fine. This allows to run the testsuite while there is another unrelated RabbitMQ node already running. Moreover, this avoids unrelated AMQP clients to mess with the testsuite node.
This commit is contained in:
parent
c39f47028b
commit
0ce5eb1bbb
|
|
@ -32,6 +32,12 @@
|
|||
-define(DEFAULT_USER, "guest").
|
||||
-define(UNAUTHORIZED_USER, "test_user_no_perm").
|
||||
-define(SSL_CERT_PASSWORD, "test").
|
||||
-define(TCP_PORTS_LIST, [
|
||||
tcp_port_amqp,
|
||||
tcp_port_amqp_tls,
|
||||
tcp_port_mgmt,
|
||||
tcp_port_erlang_dist
|
||||
]).
|
||||
|
||||
%% -------------------------------------------------------------------
|
||||
%% Testsuite internal helpers.
|
||||
|
|
@ -43,32 +49,30 @@ log_environment() ->
|
|||
[io_lib:format(" ~s~n", [V]) || V <- Vars]]).
|
||||
|
||||
run_setup_steps(Config) ->
|
||||
Checks = [
|
||||
Steps = [
|
||||
fun ensure_amqp_client_srcdir/1,
|
||||
fun ensure_amqp_client_depsdir/1,
|
||||
fun ensure_make_cmd/1,
|
||||
fun ensure_rabbitmqctl_cmd/1,
|
||||
fun ensure_nodename/1,
|
||||
fun ensure_ssl_certs/1,
|
||||
fun write_config_file/1,
|
||||
fun start_rabbitmq_node/1,
|
||||
fun create_unauthorized_user/1
|
||||
],
|
||||
run_steps(Checks, Config).
|
||||
run_steps(Config, Steps).
|
||||
|
||||
run_teardown_steps(Config) ->
|
||||
Checks = [
|
||||
Steps = [
|
||||
fun delete_unauthorized_user/1,
|
||||
fun stop_rabbitmq_node/1
|
||||
],
|
||||
run_steps(Checks, Config).
|
||||
run_steps(Config, Steps).
|
||||
|
||||
run_steps([Check | Rest], Config) ->
|
||||
case Check(Config) of
|
||||
run_steps(Config, [Step | Rest]) ->
|
||||
case Step(Config) of
|
||||
{skip, _} = Error -> Error;
|
||||
Config1 -> run_steps(Rest, Config1)
|
||||
Config1 -> run_steps(Config1, Rest)
|
||||
end;
|
||||
run_steps([], Config) ->
|
||||
run_steps(Config, []) ->
|
||||
Config.
|
||||
|
||||
ensure_amqp_client_srcdir(Config) ->
|
||||
|
|
@ -173,47 +177,6 @@ ensure_rabbitmqctl_cmd(Config) ->
|
|||
end
|
||||
end.
|
||||
|
||||
ensure_nodename(Config) ->
|
||||
Nodename = case get_config(Config, rmq_nodename) of
|
||||
undefined ->
|
||||
case os:getenv("RABBITMQ_NODENAME") of
|
||||
false ->
|
||||
Rabbitmqctl = ?config(rabbitmqctl_cmd, Config),
|
||||
Cmd = Rabbitmqctl ++ " status 2>/dev/null |" ++
|
||||
" awk '/^Status of node/ { print $4; exit; }'",
|
||||
case run_cmd_and_capture_output(Cmd) of
|
||||
{ok, Output} ->
|
||||
list_to_atom(
|
||||
string:strip(Output, both, $\n));
|
||||
{error, _} ->
|
||||
false
|
||||
end;
|
||||
N ->
|
||||
list_to_atom(string:strip(N))
|
||||
end;
|
||||
N ->
|
||||
N
|
||||
end,
|
||||
case Nodename of
|
||||
false ->
|
||||
{skip,
|
||||
"RabbitMQ nodename required, " ++
|
||||
"please set RABBITMQ_NODENAME or 'rmq_nodename' " ++
|
||||
"in ct config"};
|
||||
_ ->
|
||||
%% We test if the nodename is available. This test is
|
||||
%% incomplete because a node with that name could be running
|
||||
%% but with another cookie (so the ping will fail). But
|
||||
%% that's ok, it covers the most common situation. RabbitMQ
|
||||
%% will fail to start later for the other situation.
|
||||
case net_adm:ping(Nodename) of
|
||||
pang -> set_config(Config, {rmq_nodename, Nodename});
|
||||
pong -> {skip,
|
||||
"A node with the name '" ++ atom_to_list(Nodename) ++
|
||||
"' is already running"}
|
||||
end
|
||||
end.
|
||||
|
||||
ensure_ssl_certs(Config) ->
|
||||
Make = ?config(make_cmd, Config),
|
||||
DepsDir = ?config(amqp_client_depsdir, Config),
|
||||
|
|
@ -227,7 +190,6 @@ ensure_ssl_certs(Config) ->
|
|||
true ->
|
||||
%% Add SSL certs to the broker configuration.
|
||||
Config1 = merge_app_env(Config, rabbit, [
|
||||
{ssl_listeners, [5671]},
|
||||
{ssl_options, [
|
||||
{cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])},
|
||||
{certfile, filename:join([CertsDir, "server", "cert.pem"])},
|
||||
|
|
@ -240,29 +202,54 @@ ensure_ssl_certs(Config) ->
|
|||
{skip, "Failed to create SSL certificates"}
|
||||
end.
|
||||
|
||||
write_config_file(Config) ->
|
||||
%% Prepare a RabbitMQ configuration.
|
||||
PrivDir = ?config(priv_dir, Config),
|
||||
ConfigFile = filename:join(PrivDir, "rabbitmq"),
|
||||
ErlangConfig = ?config(erlang_node_config, Config),
|
||||
Ret = file:write_file(ConfigFile ++ ".config",
|
||||
io_lib:format("% vim:ft=erlang:~n~n~p.~n",
|
||||
[ErlangConfig])),
|
||||
case Ret of
|
||||
ok ->
|
||||
set_config(Config, {erlang_node_config_filename, ConfigFile});
|
||||
{error, Reason} ->
|
||||
{skip, "Failed to create Erlang node config file \"" ++
|
||||
ConfigFile ++ "\": " ++ file:format_error(Reason)}
|
||||
end.
|
||||
%% To start a RabbitMQ node, we need to:
|
||||
%% 1. Pick TCP port numbers
|
||||
%% 2. Generate a node name
|
||||
%% 3. Write a configuration file
|
||||
%% 4. Start the node
|
||||
%%
|
||||
%% If this fails (usually because the node name is taken or a TCP port
|
||||
%% is already in use), we start again with another set of TCP ports. The
|
||||
%% node name is derived from the AMQP TCP port so a new node name is
|
||||
%% generated.
|
||||
|
||||
start_rabbitmq_node(Config) ->
|
||||
Attempts = case get_config(Config, rmq_failed_boot_attempts) of
|
||||
undefined -> 0;
|
||||
N -> N
|
||||
end,
|
||||
Config1 = init_tcp_port_numbers(Config),
|
||||
Config2 = init_nodename(Config1),
|
||||
Config3 = init_config_filename(Config2),
|
||||
Steps = [
|
||||
fun write_config_file/1,
|
||||
fun do_start_rabbitmq_node/1
|
||||
],
|
||||
case run_steps(Config3, Steps) of
|
||||
{skip, _} = Error when Attempts >= 50 ->
|
||||
%% It's unlikely we'll ever succeed to start RabbitMQ.
|
||||
Error;
|
||||
{skip, _} ->
|
||||
%% Try again with another TCP port numbers base.
|
||||
Config4 = move_nonworking_nodedir_away(Config3),
|
||||
Config5 = set_config(Config4,
|
||||
{rmq_failed_boot_attempts, Attempts + 1}),
|
||||
start_rabbitmq_node(Config5);
|
||||
Config4 ->
|
||||
Config4
|
||||
end.
|
||||
|
||||
do_start_rabbitmq_node(Config) ->
|
||||
Make = ?config(make_cmd, Config),
|
||||
SrcDir = ?config(amqp_client_srcdir, Config),
|
||||
PrivDir = ?config(priv_dir, Config),
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
DistPort = ?config(tcp_port_erlang_dist, Config),
|
||||
ConfigFile = ?config(erlang_node_config_filename, Config),
|
||||
Cmd = Make ++ " -C " ++ SrcDir ++ make_verbosity() ++
|
||||
" start-background-node start-rabbit-on-node" ++
|
||||
" start-background-broker" ++
|
||||
" RABBITMQ_NODENAME='" ++ atom_to_list(Nodename) ++ "'" ++
|
||||
" RABBITMQ_DIST_PORT='" ++ integer_to_list(DistPort) ++ "'" ++
|
||||
" RABBITMQ_CONFIG_FILE='" ++ ConfigFile ++ "'" ++
|
||||
" TEST_TMPDIR='" ++ PrivDir ++ "'",
|
||||
case run_cmd(Cmd) of
|
||||
|
|
@ -275,10 +262,101 @@ start_rabbitmq_node(Config) ->
|
|||
false -> {skip, "Failed to initialize RabbitMQ"}
|
||||
end.
|
||||
|
||||
init_tcp_port_numbers(Config) ->
|
||||
%% If there is no TCP port numbers base previously calculated,
|
||||
%% use the TCP port 21000. If a base was previously calculated,
|
||||
%% increment it by the number of TCP ports we may open.
|
||||
%%
|
||||
%% Port 21000 is an arbitrary choice. We don't want to use the
|
||||
%% default AMQP port of 5672 so other AMQP clients on the same host
|
||||
%% do not accidentally use the testsuite broker. There seems to be
|
||||
%% no registered service around this port in /etc/services. And it
|
||||
%% should be far enough away from the default ephemeral TCP ports
|
||||
%% range.
|
||||
Base = case get_config(Config, tcp_ports_base) of
|
||||
undefined -> 21000;
|
||||
P -> P + length(?TCP_PORTS_LIST)
|
||||
end,
|
||||
Config1 = set_config(Config, {tcp_ports_base, Base}),
|
||||
%% Now, compute all TCP port numbers from this base.
|
||||
{Config2, _} = lists:foldl(
|
||||
fun(PortName, {NewConfig, NextPort}) ->
|
||||
{
|
||||
set_config(NewConfig, {PortName, NextPort}),
|
||||
NextPort + 1
|
||||
}
|
||||
end,
|
||||
{Config1, Base}, ?TCP_PORTS_LIST),
|
||||
%% Finally, update the RabbitMQ configuration with the computed TCP
|
||||
%% port numbers.
|
||||
update_tcp_ports_in_rmq_config(Config2, ?TCP_PORTS_LIST).
|
||||
|
||||
update_tcp_ports_in_rmq_config(Config, [tcp_port_amqp = Key | Rest]) ->
|
||||
Config1 = merge_app_env(Config, rabbit,
|
||||
[{tcp_listeners, [?config(Key, Config)]}]),
|
||||
update_tcp_ports_in_rmq_config(Config1, Rest);
|
||||
update_tcp_ports_in_rmq_config(Config, [tcp_port_amqp_tls = Key | Rest]) ->
|
||||
Config1 = merge_app_env(Config, rabbit,
|
||||
[{ssl_listeners, [?config(Key, Config)]}]),
|
||||
update_tcp_ports_in_rmq_config(Config1, Rest);
|
||||
update_tcp_ports_in_rmq_config(Config, [tcp_port_mgmt = Key | Rest]) ->
|
||||
Config1 = merge_app_env(Config, rabbitmq_management,
|
||||
[{listener, [{port, ?config(Key, Config)}]}]),
|
||||
update_tcp_ports_in_rmq_config(Config1, Rest);
|
||||
update_tcp_ports_in_rmq_config(Config, [tcp_port_erlang_dist | Rest]) ->
|
||||
%% The Erlang distribution port doesn't appear in the configuration file.
|
||||
update_tcp_ports_in_rmq_config(Config, Rest);
|
||||
update_tcp_ports_in_rmq_config(Config, []) ->
|
||||
Config.
|
||||
|
||||
init_nodename(Config) ->
|
||||
Base = ?config(tcp_ports_base, Config),
|
||||
Nodename = list_to_atom(rabbit_misc:format("rmq-ct-~b@localhost", [Base])),
|
||||
set_config(Config, {rmq_nodename, Nodename}).
|
||||
|
||||
init_config_filename(Config) ->
|
||||
PrivDir = ?config(priv_dir, Config),
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
ConfigDir = filename:join(PrivDir, Nodename),
|
||||
ConfigFile = filename:join(ConfigDir, Nodename),
|
||||
set_config(Config, {erlang_node_config_filename, ConfigFile}).
|
||||
|
||||
write_config_file(Config) ->
|
||||
%% Prepare a RabbitMQ configuration.
|
||||
ErlangConfig = ?config(erlang_node_config, Config),
|
||||
ConfigFile = ?config(erlang_node_config_filename, Config),
|
||||
ConfigDir = filename:dirname(ConfigFile),
|
||||
Ret1 = file:make_dir(ConfigDir),
|
||||
Ret2 = file:write_file(ConfigFile ++ ".config",
|
||||
io_lib:format("% vim:ft=erlang:~n~n~p.~n",
|
||||
[ErlangConfig])),
|
||||
case {Ret1, Ret2} of
|
||||
{ok, ok} ->
|
||||
Config;
|
||||
{{error, eexist}, ok} ->
|
||||
Config;
|
||||
{{error, Reason}, _} when Reason =/= eexist ->
|
||||
{skip, "Failed to create Erlang node config directory \"" ++
|
||||
ConfigDir ++ "\": " ++ file:format_error(Reason)};
|
||||
{_, {error, Reason}} ->
|
||||
{skip, "Failed to create Erlang node config file \"" ++
|
||||
ConfigFile ++ "\": " ++ file:format_error(Reason)}
|
||||
end.
|
||||
|
||||
move_nonworking_nodedir_away(Config) ->
|
||||
ConfigFile = ?config(erlang_node_config_filename, Config),
|
||||
ConfigDir = filename:dirname(ConfigFile),
|
||||
NewName = filename:join(
|
||||
filename:dirname(ConfigDir),
|
||||
"_unused_nodedir_" ++ filename:basename(ConfigDir)),
|
||||
file:rename(ConfigDir, NewName),
|
||||
lists:keydelete(erlang_node_config_filename, 1, Config).
|
||||
|
||||
create_unauthorized_user(Config) ->
|
||||
Rabbitmqctl = ?config(rabbitmqctl_cmd, Config),
|
||||
Cmd = Rabbitmqctl ++ " add_user " ++
|
||||
?UNAUTHORIZED_USER ++ " " ++ ?UNAUTHORIZED_USER,
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
Cmd = Rabbitmqctl ++ " -n " ++ atom_to_list(Nodename) ++
|
||||
" add_user " ++ ?UNAUTHORIZED_USER ++ " " ++ ?UNAUTHORIZED_USER,
|
||||
case run_cmd(Cmd) of
|
||||
true -> set_config(Config,
|
||||
[{rmq_unauthorized_username,
|
||||
|
|
@ -290,7 +368,9 @@ create_unauthorized_user(Config) ->
|
|||
|
||||
delete_unauthorized_user(Config) ->
|
||||
Rabbitmqctl = ?config(rabbitmqctl_cmd, Config),
|
||||
Cmd = Rabbitmqctl ++ " delete_user " ++ ?UNAUTHORIZED_USER,
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
Cmd = Rabbitmqctl ++ " -n " ++ atom_to_list(Nodename) ++
|
||||
" delete_user " ++ ?UNAUTHORIZED_USER,
|
||||
run_cmd(Cmd),
|
||||
Config.
|
||||
|
||||
|
|
@ -298,8 +378,10 @@ stop_rabbitmq_node(Config) ->
|
|||
Make = ?config(make_cmd, Config),
|
||||
SrcDir = ?config(amqp_client_srcdir, Config),
|
||||
PrivDir = ?config(priv_dir, Config),
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
Cmd = Make ++ " -C " ++ SrcDir ++ make_verbosity() ++
|
||||
" stop-rabbit-on-node stop-node" ++
|
||||
" RABBITMQ_NODENAME='" ++ atom_to_list(Nodename) ++ "'" ++
|
||||
" TEST_TMPDIR='" ++ PrivDir ++ "'",
|
||||
run_cmd(Cmd),
|
||||
Config.
|
||||
|
|
|
|||
|
|
@ -179,19 +179,22 @@ init_per_testcase(Test, Config) ->
|
|||
basic_get_ipv6_ssl -> "::1";
|
||||
_ -> ?config(rmq_hostname, Config)
|
||||
end,
|
||||
SSLOpts = if
|
||||
{Port, SSLOpts} = if
|
||||
Test =:= basic_get_ipv4_ssl orelse
|
||||
Test =:= basic_get_ipv6_ssl ->
|
||||
CertsDir = ?config(amqp_client_certsdir, Config),
|
||||
[
|
||||
{cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])},
|
||||
{certfile, filename:join([CertsDir, "client", "cert.pem"])},
|
||||
{keyfile, filename:join([CertsDir, "client", "key.pem"])},
|
||||
{verify, verify_peer},
|
||||
{fail_if_no_peer_cert, true}
|
||||
];
|
||||
{
|
||||
?config(tcp_port_amqp_tls, Config),
|
||||
[
|
||||
{cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])},
|
||||
{certfile, filename:join([CertsDir, "client", "cert.pem"])},
|
||||
{keyfile, filename:join([CertsDir, "client", "key.pem"])},
|
||||
{verify, verify_peer},
|
||||
{fail_if_no_peer_cert, true}
|
||||
]
|
||||
};
|
||||
true ->
|
||||
none
|
||||
{?config(tcp_port_amqp, Config), none}
|
||||
end,
|
||||
ChannelMax = case Test of
|
||||
channel_tune_negotiation -> 10;
|
||||
|
|
@ -209,6 +212,7 @@ init_per_testcase(Test, Config) ->
|
|||
username = Username,
|
||||
password = Password,
|
||||
host = Hostname,
|
||||
port = Port,
|
||||
virtual_host = VHost,
|
||||
channel_max = ChannelMax,
|
||||
ssl_options = SSLOpts}
|
||||
|
|
@ -1374,27 +1378,35 @@ assert_down_with_error(MonitorRef, CodeAtom) ->
|
|||
set_resource_alarm(memory, Config) ->
|
||||
Make = ?config(make_cmd, Config),
|
||||
SrcDir = ?config(amqp_client_srcdir, Config),
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
true = rabbit_ct_helpers:run_cmd(
|
||||
Make ++ " -C " ++ SrcDir ++ rabbit_ct_helpers:make_verbosity() ++
|
||||
" RABBITMQ_NODENAME='" ++ atom_to_list(Nodename) ++ "'" ++
|
||||
" set-resource-alarm SOURCE=memory");
|
||||
set_resource_alarm(disk, Config) ->
|
||||
Make = ?config(make_cmd, Config),
|
||||
SrcDir = ?config(amqp_client_srcdir, Config),
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
true = rabbit_ct_helpers:run_cmd(
|
||||
Make ++ " -C " ++ SrcDir ++ rabbit_ct_helpers:make_verbosity() ++
|
||||
" RABBITMQ_NODENAME='" ++ atom_to_list(Nodename) ++ "'" ++
|
||||
" set-resource-alarm SOURCE=disk").
|
||||
|
||||
clear_resource_alarm(memory, Config) ->
|
||||
Make = ?config(make_cmd, Config),
|
||||
SrcDir = ?config(amqp_client_srcdir, Config),
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
true = rabbit_ct_helpers:run_cmd(
|
||||
Make ++ " -C " ++ SrcDir ++ rabbit_ct_helpers:make_verbosity() ++
|
||||
" RABBITMQ_NODENAME='" ++ atom_to_list(Nodename) ++ "'" ++
|
||||
" clear-resource-alarm SOURCE=memory");
|
||||
clear_resource_alarm(disk, Config) ->
|
||||
Make = ?config(make_cmd, Config),
|
||||
SrcDir = ?config(amqp_client_srcdir, Config),
|
||||
Nodename = ?config(rmq_nodename, Config),
|
||||
true = rabbit_ct_helpers:run_cmd(
|
||||
Make ++ " -C " ++ SrcDir ++ rabbit_ct_helpers:make_verbosity() ++
|
||||
" RABBITMQ_NODENAME='" ++ atom_to_list(Nodename) ++ "'" ++
|
||||
" clear-resource-alarm SOURCE=disk").
|
||||
|
||||
fmt(Fmt, Args) -> list_to_binary(rabbit_misc:format(Fmt, Args)).
|
||||
|
|
|
|||
Loading…
Reference in New Issue