rabbitmq-server/deps/rabbit/test/per_user_connection_trackin...

275 lines
10 KiB
Erlang

%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%
%% Copyright (c) 2007-2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
%%
-module(per_user_connection_tracking_SUITE).
-include_lib("rabbitmq_ct_helpers/include/rabbit_assert.hrl").
-include_lib("common_test/include/ct.hrl").
-include_lib("amqp_client/include/amqp_client.hrl").
-include_lib("eunit/include/eunit.hrl").
-compile(nowarn_export_all).
-compile(export_all).
-define(AWAIT_TIMEOUT, 30000).
all() ->
[
{group, tests}
].
groups() ->
ClusterSize1Tests = [
single_node_list_of_user,
single_node_user_deletion_forces_connection_closure
],
ClusterSize2Tests = [
cluster_user_deletion_forces_connection_closure
],
[
{tests, [], [
{cluster_size_1_network, [], ClusterSize1Tests},
{cluster_size_2_network, [], ClusterSize2Tests},
{cluster_size_1_direct, [], ClusterSize1Tests},
{cluster_size_2_direct, [], ClusterSize2Tests}
]}
].
suite() ->
[
%% If a test hangs, no need to wait for 30 minutes.
{timetrap, {minutes, 8}}
].
%% -------------------------------------------------------------------
%% Testsuite setup/teardown.
%% -------------------------------------------------------------------
init_per_suite(Config) ->
rabbit_ct_helpers:log_environment(),
rabbit_ct_helpers:run_setup_steps(Config).
end_per_suite(Config) ->
rabbit_ct_helpers:run_teardown_steps(Config).
init_per_group(cluster_size_1_network, Config) ->
Config1 = rabbit_ct_helpers:set_config(Config, [{connection_type, network}]),
init_per_multinode_group(cluster_size_1_network, Config1, 1);
init_per_group(cluster_size_2_network, Config) ->
Config1 = rabbit_ct_helpers:set_config(Config, [{connection_type, network}]),
init_per_multinode_group(cluster_size_2_network, Config1, 2);
init_per_group(cluster_size_1_direct, Config) ->
Config1 = rabbit_ct_helpers:set_config(Config, [{connection_type, direct}]),
init_per_multinode_group(cluster_size_1_direct, Config1, 1);
init_per_group(cluster_size_2_direct, Config) ->
Config1 = rabbit_ct_helpers:set_config(Config, [{connection_type, direct}]),
init_per_multinode_group(cluster_size_2_direct, Config1, 2);
init_per_group(tests, Config) ->
Config.
init_per_multinode_group(_Group, Config, NodeCount) ->
Suffix = rabbit_ct_helpers:testcase_absname(Config, "", "-"),
Config1 = rabbit_ct_helpers:set_config(Config, [
{rmq_nodes_count, NodeCount},
{rmq_nodename_suffix, Suffix}
]),
rabbit_ct_helpers:run_steps(Config1,
rabbit_ct_broker_helpers:setup_steps() ++
rabbit_ct_client_helpers:setup_steps()).
end_per_group(tests, Config) ->
% The broker is managed by {init,end}_per_testcase().
Config;
end_per_group(_Group, Config) ->
rabbit_ct_helpers:run_steps(Config,
rabbit_ct_client_helpers:teardown_steps() ++
rabbit_ct_broker_helpers:teardown_steps()).
init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase),
Config.
end_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_finished(Config, Testcase).
%% -------------------------------------------------------------------
%% Test cases.
%% -------------------------------------------------------------------
single_node_list_of_user(Config) ->
Username1 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-1"),
Username2 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-2"),
Vhost = proplists:get_value(rmq_vhost, Config),
[ begin
rabbit_ct_broker_helpers:add_user(Config, U),
rabbit_ct_broker_helpers:set_full_permissions(Config, U, Vhost)
end || U <- [Username1, Username2]],
?assertEqual(0, count_connections_in(Config, Username1)),
?assertEqual(0, count_connections_in(Config, Username2)),
[Conn1] = open_connections(Config, [{0, Username1}]),
?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
?awaitMatch(
[#tracked_connection{username = Username1}],
connections_in(Config, Username1),
?AWAIT_TIMEOUT),
close_connections([Conn1]),
?awaitMatch(0, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[Conn2] = open_connections(Config, [{0, Username2}]),
?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
?awaitMatch(
[#tracked_connection{username = Username2}],
connections_in(Config, Username2),
?AWAIT_TIMEOUT),
[Conn3] = open_connections(Config, [{0, Username1}]),
?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
?awaitMatch(
[#tracked_connection{username = Username1}],
connections_in(Config, Username1),
?AWAIT_TIMEOUT),
[Conn4] = open_connections(Config, [{0, Username1}]),
kill_connections([Conn4]),
?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
?awaitMatch(
[#tracked_connection{username = Username1}],
connections_in(Config, Username1),
?AWAIT_TIMEOUT),
[Conn5] = open_connections(Config, [{0, Username1}]),
?awaitMatch(2, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
?awaitMatch(
[Username1, Username1],
lists:map(fun (#tracked_connection{username = U}) -> U end,
connections_in(Config, Username1)),
?AWAIT_TIMEOUT),
close_connections([Conn2, Conn3, Conn5]),
rabbit_ct_broker_helpers:delete_user(Config, Username2),
?awaitMatch(0, length(all_connections(Config)), ?AWAIT_TIMEOUT).
single_node_user_deletion_forces_connection_closure(Config) ->
Username1 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-1"),
Username2 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-2"),
Vhost = proplists:get_value(rmq_vhost, Config),
[ begin
rabbit_ct_broker_helpers:add_user(Config, U),
rabbit_ct_broker_helpers:set_full_permissions(Config, U, Vhost)
end || U <- [Username1, Username2]],
?assertEqual(0, count_connections_in(Config, Username1)),
?assertEqual(0, count_connections_in(Config, Username2)),
[Conn1] = open_connections(Config, [{0, Username1}]),
?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[_Conn2] = open_connections(Config, [{0, Username2}]),
?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
rabbit_ct_broker_helpers:delete_user(Config, Username2),
?awaitMatch(0, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
close_connections([Conn1]),
?awaitMatch(0, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT).
cluster_user_deletion_forces_connection_closure(Config) ->
Username1 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-1"),
Username2 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-2"),
Vhost = proplists:get_value(rmq_vhost, Config),
[ begin
rabbit_ct_broker_helpers:add_user(Config, U),
rabbit_ct_broker_helpers:set_full_permissions(Config, U, Vhost)
end || U <- [Username1, Username2]],
?assertEqual(0, count_connections_in(Config, Username1)),
?assertEqual(0, count_connections_in(Config, Username2)),
[Conn1] = open_connections(Config, [{0, Username1}]),
?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[_Conn2] = open_connections(Config, [{1, Username2}]),
?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
rabbit_ct_broker_helpers:delete_user(Config, Username2),
?awaitMatch(0, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
close_connections([Conn1]),
?awaitMatch(0, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT).
%% -------------------------------------------------------------------
%% Helpers
%% -------------------------------------------------------------------
open_connections(Config, NodesAndUsers) ->
% Randomly select connection type
OpenConnectionFun = case ?config(connection_type, Config) of
network -> open_unmanaged_connection;
direct -> open_unmanaged_connection_direct
end,
Conns = lists:map(fun
({Node, User}) ->
rabbit_ct_client_helpers:OpenConnectionFun(Config, Node,
User, User);
(Node) ->
rabbit_ct_client_helpers:OpenConnectionFun(Config, Node)
end, NodesAndUsers),
Conns.
close_connections(Conns) ->
lists:foreach(fun
(Conn) ->
rabbit_ct_client_helpers:close_connection(Conn)
end, Conns).
kill_connections(Conns) ->
lists:foreach(fun
(Conn) ->
(catch exit(Conn, please_terminate))
end, Conns).
count_connections_in(Config, Username) ->
length(connections_in(Config, Username)).
connections_in(Config, Username) ->
connections_in(Config, 0, Username).
connections_in(Config, NodeIndex, Username) ->
rabbit_ct_broker_helpers:rpc(Config, NodeIndex,
rabbit_connection_tracking,
list_of_user, [Username]).
all_connections(Config) ->
all_connections(Config, 0).
all_connections(Config, NodeIndex) ->
rabbit_ct_broker_helpers:rpc(Config, NodeIndex,
rabbit_connection_tracking,
list, []).
set_up_vhost(Config, VHost) ->
rabbit_ct_broker_helpers:add_vhost(Config, VHost),
rabbit_ct_broker_helpers:set_full_permissions(Config, <<"guest">>, VHost),
set_vhost_connection_limit(Config, VHost, -1).
set_vhost_connection_limit(Config, VHost, Count) ->
set_vhost_connection_limit(Config, 0, VHost, Count).
set_vhost_connection_limit(Config, NodeIndex, VHost, Count) ->
Node = rabbit_ct_broker_helpers:get_node_config(
Config, NodeIndex, nodename),
ok = rabbit_ct_broker_helpers:control_action(
set_vhost_limits, Node,
["{\"max-connections\": " ++ integer_to_list(Count) ++ "}"],
[{"-p", binary_to_list(VHost)}]).