Skip to content

Commit 2d96e7c

Browse files
Merge pull request #7799 from rabbitmq/rabbitmq-server-7777-for-3.11.x
Introduce a limit for how many virtual hosts can be created in a cluster (adapted for 3.11.x)
2 parents 7a5401d + 1ffd73e commit 2d96e7c

File tree

7 files changed

+116
-24
lines changed

7 files changed

+116
-24
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]}, {validators, ["non_negative_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_queue_type.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
-type arguments() :: queue_arguments | consumer_arguments.
5858
-type queue_type() :: rabbit_classic_queue | rabbit_quorum_queue | rabbit_stream_queue.
5959

60+
-export_type([queue_type/0]).
61+
6062
-define(STATE, ?MODULE).
6163

6264
%% Recoverable mirrors shouldn't really be a generic one, but let's keep it here until

deps/rabbit/src/rabbit_vhost.erl

Lines changed: 34 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

@@ -286,9 +287,22 @@ delete(VHost, ActingUser) ->
286287
rabbit_vhost_sup_sup:delete_on_all_nodes(VHost),
287288
ok.
288289

290+
-spec put_vhost(vhost:name(),
291+
binary(),
292+
vhost:tags(),
293+
boolean(),
294+
rabbit_types:username()) ->
295+
'ok' | {'error', any()} | {'EXIT', any()}.
289296
put_vhost(Name, Description, Tags0, Trace, Username) ->
290297
put_vhost(Name, Description, Tags0, undefined, Trace, Username).
291298

299+
-spec put_vhost(vhost:name(),
300+
binary(),
301+
vhost:unparsed_tags() | vhost:tags(),
302+
rabbit_queue_type:queue_type() | 'undefined',
303+
boolean(),
304+
rabbit_types:username()) ->
305+
'ok' | {'error', any()} | {'EXIT', any()}.
292306
put_vhost(Name, Description, Tags0, DefaultQueueType, Trace, Username) ->
293307
Tags = case Tags0 of
294308
undefined -> <<"">>;
@@ -333,6 +347,26 @@ put_vhost(Name, Description, Tags0, DefaultQueueType, Trace, Username) ->
333347
end,
334348
Result.
335349

350+
-spec is_over_vhost_limit(vhost:name()) -> 'ok' | no_return().
351+
is_over_vhost_limit(Name) ->
352+
Limit = rabbit_misc:get_env(rabbit, vhost_max, infinity),
353+
is_over_vhost_limit(Name, Limit).
354+
355+
-spec is_over_vhost_limit(vhost:name(), 'infinity' | non_neg_integer())
356+
-> 'ok' | no_return().
357+
is_over_vhost_limit(_Name, infinity) ->
358+
ok;
359+
is_over_vhost_limit(Name, Limit) when is_integer(Limit) ->
360+
case length(list_names()) >= Limit of
361+
false ->
362+
ok;
363+
true ->
364+
ErrorMsg = rabbit_misc:format("cannot create vhost '~ts': "
365+
"vhost limit of ~tp is reached",
366+
[Name, Limit]),
367+
exit({vhost_limit_exceeded, ErrorMsg})
368+
end.
369+
336370
%% when definitions are loaded on boot, Username here will be ?INTERNAL_USER,
337371
%% which does not actually exist
338372
maybe_grant_full_permissions(_Name, ?INTERNAL_USER) ->

deps/rabbit/src/vhost.erl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
tags => [atom()],
4343
metadata_key() => any()} | undefined).
4444

45+
-type(description() :: binary()).
46+
-type(tag() :: atom()).
47+
-type(tags() :: [tag()]).
48+
-type(unparsed_tags() :: binary() | string() | atom()).
49+
4550
-type vhost() :: vhost_v2().
4651

4752
-record(vhost, {
@@ -68,6 +73,10 @@
6873
-export_type([name/0,
6974
metadata_key/0,
7075
metadata/0,
76+
description/0,
77+
tag/0,
78+
unparsed_tags/0,
79+
tags/0,
7180
vhost/0,
7281
vhost_v2/0,
7382
vhost_pattern/0,

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_limit_exceeded, _}} = 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_limit_exceeded, _}} = 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_limit_exceeded, 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: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
-export([init/2, resource_exists/2, to_json/2,
1111
content_types_provided/2, content_types_accepted/2,
1212
is_authorized/2, allowed_methods/2, accept_content/2,
13-
delete_resource/2, id/1, put_vhost/6]).
13+
delete_resource/2, id/1]).
1414
-export([variances/2]).
1515

1616
-import(rabbit_misc, [pget/2]).
1717

1818
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
1919
-include_lib("rabbit_common/include/rabbit.hrl").
2020

21+
-dialyzer({nowarn_function, accept_content/2}).
22+
2123
%%--------------------------------------------------------------------
2224

2325
init(Req, _State) ->
@@ -60,25 +62,28 @@ accept_content(ReqData0, Context = #context{user = #user{username = Username}})
6062
rabbit_mgmt_util:with_decode(
6163
[], ReqData0, Context,
6264
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
65+
Trace = rabbit_mgmt_util:parse_bool(maps:get(tracing, BodyMap, undefined)),
66+
Description = maps:get(description, BodyMap, <<"">>),
67+
Tags = maps:get(tags, BodyMap, <<"">>),
68+
%% defaultqueuetype was an unfortunate name picked originally for 3.11.0,
69+
%% so fall back to it. See rabbitmq/rabbitmq-server#7734.
70+
FallbackQT = maps:get(defaultqueuetype, BodyMap, undefined),
71+
DefaultQT = maps:get(default_queue_type, BodyMap, FallbackQT),
72+
case rabbit_vhost:put_vhost(Name, Description, Tags, DefaultQT, Trace, Username) of
73+
ok ->
74+
{true, ReqData, Context};
75+
{error, timeout} = E ->
76+
rabbit_mgmt_util:internal_server_error(
77+
"Timed out while waiting for the vhost to initialise", E,
78+
ReqData0, Context);
79+
{error, E} ->
80+
rabbit_mgmt_util:internal_server_error(
81+
"Error occured while adding vhost", E,
82+
ReqData0, Context);
83+
{'EXIT', {vhost_limit_exceeded,
84+
Explanation}} ->
85+
rabbit_mgmt_util:bad_request(list_to_binary(Explanation), ReqData, Context)
86+
end
8287
end).
8388

8489
delete_resource(ReqData, Context = #context{user = #user{username = Username}}) ->
@@ -101,5 +106,3 @@ id(ReqData) ->
101106
Value -> Value
102107
end.
103108

104-
put_vhost(Name, Description, Tags, DefaultQT, Trace, Username) ->
105-
rabbit_vhost:put_vhost(Name, Description, Tags, DefaultQT, Trace, Username).

0 commit comments

Comments
 (0)