Skip to content

Commit 22d6c6c

Browse files
SimonUngemichaelklishin
authored andcommitted
See #7777. Use vhost_max to stop vhost creation in rabbitmq
(cherry picked from commit 574ca55)
1 parent 7a5401d commit 22d6c6c

File tree

5 files changed

+86
-21
lines changed

5 files changed

+86
-21
lines changed

deps/rabbit/priv/schema/rabbit.schema

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,19 @@ end}.
897897
end
898898
}.
899899

900+
{mapping, "vhost_max", "rabbit.vhost_max",
901+
[{datatype, [{atom, infinity}, integer]}]}.
902+
903+
{translation, "rabbit.vhost_max",
904+
fun(Conf) ->
905+
case cuttlefish:conf_get("vhost_max", Conf, undefined) of
906+
undefined -> cuttlefish:unset();
907+
infinity -> infinity;
908+
Val when is_integer(Val) -> Val;
909+
_ -> cuttlefish:invalid("should be a non-negative integer")
910+
end
911+
end
912+
}.
900913

901914
{mapping, "max_message_size", "rabbit.max_message_size",
902915
[{datatype, integer}, {validators, ["max_message_size"]}]}.

deps/rabbit/src/rabbit_vhost.erl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ add(Name, Metadata, ActingUser) ->
161161
end.
162162

163163
do_add(Name, Metadata, ActingUser) ->
164+
ok = is_over_vhost_limit(Name),
164165
Description = maps:get(description, Metadata, undefined),
165166
Tags = maps:get(tags, Metadata, []),
166167

@@ -333,6 +334,23 @@ put_vhost(Name, Description, Tags0, DefaultQueueType, Trace, Username) ->
333334
end,
334335
Result.
335336

337+
is_over_vhost_limit(Name) ->
338+
Limit = rabbit_misc:get_env(rabbit, vhost_max, infinity),
339+
is_over_vhost_limit(Name, Limit).
340+
341+
is_over_vhost_limit(_Name, infinity) ->
342+
ok;
343+
is_over_vhost_limit(Name, Limit) when is_integer(Limit) ->
344+
case length(list_names()) >= Limit of
345+
false ->
346+
ok;
347+
true ->
348+
ErrorMsg = rabbit_misc:format("cannot create vhost '~ts': "
349+
"vhost limit '~tp' is reached",
350+
[Name, Limit]),
351+
exit({vhost_precondition_failed, ErrorMsg})
352+
end.
353+
336354
%% when definitions are loaded on boot, Username here will be ?INTERNAL_USER,
337355
%% which does not actually exist
338356
maybe_grant_full_permissions(_Name, ?INTERNAL_USER) ->

deps/rabbit/test/per_node_limit_SUITE.erl

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ all() ->
2121
groups() ->
2222
[
2323
{parallel_tests, [parallel], [
24-
node_connection_limit
24+
node_connection_limit,
25+
vhost_limit
2526
]}
2627
].
2728

@@ -58,6 +59,9 @@ end_per_group(_Group, Config) ->
5859
init_per_testcase(Testcase, Config) ->
5960
rabbit_ct_helpers:testcase_started(Config, Testcase).
6061

62+
end_per_testcase(vhost_limit = Testcase, Config) ->
63+
[rabbit_ct_broker_helpers:delete_vhost(Config, integer_to_binary(I)) || I <- lists:seq(1,4)],
64+
rabbit_ct_helpers:testcase_finished(Config, Testcase);
6165
end_per_testcase(Testcase, Config) ->
6266
rabbit_ct_helpers:testcase_finished(Config, Testcase).
6367

@@ -78,7 +82,25 @@ node_connection_limit(Config) ->
7882

7983
set_node_limit(Config, infinity),
8084
C = rabbit_ct_client_helpers:open_unmanaged_connection(Config, 0),
81-
true = is_pid(C).
85+
true = is_pid(C),
86+
close_all_connections([C]),
87+
ok.
88+
89+
vhost_limit(Config) ->
90+
set_vhost_limit(Config, 0),
91+
{'EXIT',{vhost_precondition_failed, _}} = rabbit_ct_broker_helpers:add_vhost(Config, <<"foo">>),
92+
93+
set_vhost_limit(Config, 5),
94+
[ok = rabbit_ct_broker_helpers:add_vhost(Config, integer_to_binary(I)) || I <- lists:seq(1,4)],
95+
{'EXIT',{vhost_precondition_failed, _}} = rabbit_ct_broker_helpers:add_vhost(Config, <<"5">>),
96+
[rabbit_ct_broker_helpers:delete_vhost(Config, integer_to_binary(I)) || I <- lists:seq(1,4)],
97+
98+
set_vhost_limit(Config, infinity),
99+
[ok = rabbit_ct_broker_helpers:add_vhost(Config, integer_to_binary(I)) || I <- lists:seq(1,4)],
100+
ok = rabbit_ct_broker_helpers:add_vhost(Config, <<"5">>),
101+
[rabbit_ct_broker_helpers:delete_vhost(Config, integer_to_binary(I)) || I <- lists:seq(1,5)],
102+
ok.
103+
82104

83105
%% -------------------------------------------------------------------
84106
%% Implementation
@@ -97,3 +119,8 @@ set_node_limit(Config, Limit) ->
97119
rabbit_ct_broker_helpers:rpc(Config, 0,
98120
application,
99121
set_env, [rabbit, connection_max, Limit]).
122+
123+
set_vhost_limit(Config, Limit) ->
124+
rabbit_ct_broker_helpers:rpc(Config, 0,
125+
application,
126+
set_env, [rabbit, vhost_max, Limit]).

deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/add_vhost_command.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddVhostCommand do
7272
{:error, ExitCodes.exit_usage(), "Unsupported default queue type"}
7373
end
7474

75+
def output({:badrpc, {:EXIT, {:vhost_precondition_failed, msg}}}, _opts) do
76+
{:error, ExitCodes.exit_usage(), msg}
77+
end
78+
7579
use RabbitMQ.CLI.DefaultOutput
7680

7781
def usage,

deps/rabbitmq_management/src/rabbit_mgmt_wm_vhost.erl

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,28 @@ accept_content(ReqData0, Context = #context{user = #user{username = Username}})
6060
rabbit_mgmt_util:with_decode(
6161
[], ReqData0, Context,
6262
fun(_, BodyMap, ReqData) ->
63-
Trace = rabbit_mgmt_util:parse_bool(maps:get(tracing, BodyMap, undefined)),
64-
Description = maps:get(description, BodyMap, <<"">>),
65-
Tags = maps:get(tags, BodyMap, <<"">>),
66-
%% defaultqueuetype was an unfortunate name picked originally for 3.11.0,
67-
%% so fall back to it. See rabbitmq/rabbitmq-server#7734.
68-
FallbackQT = maps:get(defaultqueuetype, BodyMap, undefined),
69-
DefaultQT = maps:get(default_queue_type, BodyMap, FallbackQT),
70-
case put_vhost(Name, Description, Tags, DefaultQT, Trace, Username) of
71-
ok ->
72-
{true, ReqData, Context};
73-
{error, timeout} = E ->
74-
rabbit_mgmt_util:internal_server_error(
75-
"Timed out while waiting for the vhost to initialise", E,
76-
ReqData0, Context);
77-
{error, E} ->
78-
rabbit_mgmt_util:internal_server_error(
79-
"Error occured while adding vhost", E,
80-
ReqData0, Context)
81-
end
63+
Trace = rabbit_mgmt_util:parse_bool(maps:get(tracing, BodyMap, undefined)),
64+
Description = maps:get(description, BodyMap, <<"">>),
65+
Tags = maps:get(tags, BodyMap, <<"">>),
66+
%% defaultqueuetype was an unfortunate name picked originally for 3.11.0,
67+
%% so fall back to it. See rabbitmq/rabbitmq-server#7734.
68+
FallbackQT = maps:get(defaultqueuetype, BodyMap, undefined),
69+
DefaultQT = maps:get(default_queue_type, BodyMap, FallbackQT),
70+
case put_vhost(Name, Description, Tags, DefaultQT, Trace, Username) of
71+
ok ->
72+
{true, ReqData, Context};
73+
{'EXIT', {vhost_precondition_failed,
74+
Explanation}} ->
75+
rabbit_mgmt_util:bad_request(list_to_binary(Explanation), ReqData, Context);
76+
{error, timeout} = E ->
77+
rabbit_mgmt_util:internal_server_error(
78+
"Timed out while waiting for the vhost to initialise", E,
79+
ReqData0, Context);
80+
{error, E} ->
81+
rabbit_mgmt_util:internal_server_error(
82+
"Error occured while adding vhost", E,
83+
ReqData0, Context)
84+
end
8285
end).
8386

8487
delete_resource(ReqData, Context = #context{user = #user{username = Username}}) ->

0 commit comments

Comments
 (0)