4459 lines
203 KiB
Erlang
4459 lines
203 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(rabbit_mgmt_http_SUITE).
|
|
|
|
-include_lib("amqp_client/include/amqp_client.hrl").
|
|
-include_lib("amqp10_common/include/amqp10_filter.hrl").
|
|
-include_lib("amqp10_client/include/amqp10_client.hrl").
|
|
-include_lib("common_test/include/ct.hrl").
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
-include_lib("rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl").
|
|
-include_lib("rabbitmq_ct_helpers/include/rabbit_assert.hrl").
|
|
|
|
-import(rabbit_ct_client_helpers, [close_connection/1, close_channel/1,
|
|
open_unmanaged_connection/1]).
|
|
-import(rabbit_ct_broker_helpers, [rpc/4]).
|
|
-import(rabbit_ct_helpers,
|
|
[eventually/1,
|
|
eventually/3]).
|
|
-import(rabbit_mgmt_test_util, [assert_list/2, assert_item/2, test_item/2,
|
|
assert_keys/2, assert_no_keys/2,
|
|
decode_body/1,
|
|
http_get/2, http_get/3, http_get/5,
|
|
http_get_no_auth/3,
|
|
http_get_no_decode/5,
|
|
http_put/4, http_put/6,
|
|
http_post/4, http_post/6,
|
|
http_post_json/4,
|
|
http_upload_raw/8,
|
|
http_delete/3, http_delete/4, http_delete/5,
|
|
http_put_raw/4, http_post_accept_json/4,
|
|
req/4, auth_header/2,
|
|
assert_permanent_redirect/3,
|
|
uri_base_from/2, format_for_upload/1,
|
|
amqp_port/1, req/6]).
|
|
|
|
-import(rabbit_misc, [pget/2]).
|
|
|
|
-define(COLLECT_INTERVAL, 256).
|
|
-define(PATH_PREFIX, "/custom-prefix").
|
|
|
|
-define(AWAIT(Body),
|
|
await_condition(fun () -> Body end)).
|
|
|
|
-compile([export_all, nowarn_export_all]).
|
|
|
|
all() ->
|
|
[
|
|
{group, all_tests_with_prefix},
|
|
{group, all_tests_without_prefix},
|
|
{group, definitions_group1_without_prefix},
|
|
{group, definitions_group2_without_prefix},
|
|
{group, definitions_group3_without_prefix},
|
|
{group, definitions_group4_without_prefix},
|
|
{group, default_queue_type_without_prefix}
|
|
].
|
|
|
|
groups() ->
|
|
[
|
|
{all_tests_with_prefix, [], some_tests() ++ all_tests()},
|
|
{all_tests_without_prefix, [], some_tests()},
|
|
%% We have several groups because their interference is
|
|
%% way above average. It is easier to use separate groups
|
|
%% that get a blank node each than to try to untangle multiple
|
|
%% definitions-related tests. MK.
|
|
{definitions_group1_without_prefix, [], definitions_group1_tests()},
|
|
{definitions_group2_without_prefix, [], definitions_group2_tests()},
|
|
{definitions_group3_without_prefix, [], definitions_group3_tests()},
|
|
{definitions_group4_without_prefix, [], definitions_group4_tests()},
|
|
{default_queue_type_without_prefix, [], default_queue_type_group_tests()}
|
|
].
|
|
|
|
some_tests() ->
|
|
[
|
|
users_test,
|
|
users_protected_test,
|
|
exchanges_test,
|
|
queues_test,
|
|
bindings_test,
|
|
policy_test,
|
|
policy_permissions_test
|
|
].
|
|
|
|
definitions_group1_tests() ->
|
|
[
|
|
definitions_test,
|
|
definitions_password_test,
|
|
long_definitions_test,
|
|
long_definitions_multipart_test
|
|
].
|
|
|
|
definitions_group2_tests() ->
|
|
[
|
|
definitions_default_queue_type_test,
|
|
definitions_vhost_metadata_test,
|
|
definitions_file_metadata_test
|
|
].
|
|
|
|
definitions_group3_tests() ->
|
|
[
|
|
definitions_server_named_queue_test,
|
|
definitions_with_charset_test
|
|
].
|
|
|
|
definitions_group4_tests() ->
|
|
[
|
|
definitions_vhost_test
|
|
].
|
|
|
|
default_queue_type_group_tests() ->
|
|
[
|
|
default_queue_type_fallback_in_overview_test,
|
|
default_queue_type_with_value_configured_in_overview_test,
|
|
default_queue_types_in_vhost_list_test,
|
|
default_queue_type_of_one_vhost_test
|
|
].
|
|
|
|
all_tests() -> [
|
|
cli_redirect_test,
|
|
api_redirect_test,
|
|
stats_redirect_test,
|
|
overview_test,
|
|
auth_test,
|
|
cluster_name_test,
|
|
nodes_test,
|
|
memory_test,
|
|
ets_tables_memory_test,
|
|
vhosts_test,
|
|
vhosts_description_test,
|
|
vhosts_trace_test,
|
|
users_legacy_administrator_test,
|
|
adding_a_user_with_password_test,
|
|
adding_a_user_with_password_hash_test,
|
|
adding_a_user_with_generated_password_hash_test,
|
|
adding_a_user_with_permissions_in_single_operation_test,
|
|
adding_a_user_without_tags_fails_test,
|
|
adding_a_user_without_password_or_hash_test,
|
|
adding_a_user_with_both_password_and_hash_fails_test,
|
|
updating_a_user_without_password_or_hash_clears_password_test,
|
|
user_credential_validation_accept_everything_succeeds_test,
|
|
user_credential_validation_min_length_succeeds_test,
|
|
user_credential_validation_min_length_fails_test,
|
|
updating_tags_of_a_passwordless_user_test,
|
|
permissions_validation_test,
|
|
permissions_list_test,
|
|
permissions_test,
|
|
multiple_invalid_connections_test,
|
|
quorum_queues_test,
|
|
stream_queues_have_consumers_field,
|
|
bindings_post_test,
|
|
bindings_null_routing_key_test,
|
|
bindings_e2e_test,
|
|
permissions_administrator_test,
|
|
permissions_vhost_test,
|
|
permissions_amqp_test,
|
|
permissions_queue_delete_test,
|
|
permissions_connection_channel_consumer_test,
|
|
consumers_cq_test,
|
|
consumers_qq_test,
|
|
arguments_test,
|
|
arguments_table_test,
|
|
queue_purge_test,
|
|
queue_actions_test,
|
|
exclusive_consumer_test,
|
|
exclusive_queue_test,
|
|
connections_channels_pagination_test,
|
|
exchanges_pagination_test,
|
|
exchanges_pagination_permissions_test,
|
|
queues_detailed_test,
|
|
queue_pagination_test,
|
|
queue_pagination_columns_test,
|
|
queues_pagination_permissions_test,
|
|
samples_range_test,
|
|
sorting_test,
|
|
format_output_test,
|
|
columns_test,
|
|
get_test,
|
|
get_encoding_test,
|
|
get_fail_test,
|
|
publish_test,
|
|
publish_large_message_test,
|
|
publish_large_message_exceeding_http_request_body_size_test,
|
|
publish_accept_json_test,
|
|
publish_fail_test,
|
|
publish_base64_test,
|
|
publish_unrouted_test,
|
|
if_empty_unused_test,
|
|
parameters_test,
|
|
global_parameters_test,
|
|
disabled_operator_policy_test,
|
|
operator_policy_test,
|
|
issue67_test,
|
|
extensions_test,
|
|
cors_test,
|
|
vhost_limits_list_test,
|
|
vhost_limit_set_test,
|
|
rates_test,
|
|
single_active_consumer_cq_test,
|
|
single_active_consumer_qq_test,
|
|
%% This test needs the OAuth 2 plugin to be enabled
|
|
%% oauth_test,
|
|
disable_basic_auth_test,
|
|
login_test,
|
|
csp_headers_test,
|
|
auth_attempts_test,
|
|
user_limits_list_test,
|
|
user_limit_set_test,
|
|
config_environment_test,
|
|
disabled_qq_replica_opers_test,
|
|
qq_status_test,
|
|
list_deprecated_features_test,
|
|
list_used_deprecated_features_test,
|
|
connections_amqpl,
|
|
connections_amqp,
|
|
amqp_sessions,
|
|
amqpl_sessions,
|
|
enable_plugin_amqp,
|
|
cluster_and_node_tags_test,
|
|
version_test
|
|
].
|
|
|
|
%% -------------------------------------------------------------------
|
|
%% Testsuite setup/teardown.
|
|
%% -------------------------------------------------------------------
|
|
merge_app_env(Config) ->
|
|
Config1 = rabbit_ct_helpers:merge_app_env(Config,
|
|
{rabbit,
|
|
[
|
|
{collect_statistics_interval,
|
|
?COLLECT_INTERVAL},
|
|
{quorum_tick_interval, 256},
|
|
{stream_tick_interval, 256}
|
|
]}),
|
|
rabbit_ct_helpers:merge_app_env(Config1,
|
|
{rabbitmq_management, [
|
|
{sample_retention_policies,
|
|
[{global, [{605, 1}]},
|
|
{basic, [{605, 1}]},
|
|
{detailed, [{10, 1}]}]
|
|
}]}).
|
|
|
|
start_broker(Config) ->
|
|
Setup0 = rabbit_ct_broker_helpers:setup_steps(),
|
|
Setup1 = rabbit_ct_client_helpers:setup_steps(),
|
|
Steps = Setup0 ++ Setup1,
|
|
case rabbit_ct_helpers:run_setup_steps(Config, Steps) of
|
|
{skip, _} = Skip ->
|
|
Skip;
|
|
Config1 ->
|
|
Ret = rabbit_ct_broker_helpers:enable_feature_flag(
|
|
Config1, 'rabbitmq_4.0.0'),
|
|
case Ret of
|
|
ok -> Config1;
|
|
_ -> Ret
|
|
end
|
|
end.
|
|
|
|
finish_init(Group, Config) ->
|
|
rabbit_ct_helpers:log_environment(),
|
|
inets:start(),
|
|
NodeConf = [{rmq_nodename_suffix, Group}],
|
|
Config1 = rabbit_ct_helpers:set_config(Config, NodeConf),
|
|
merge_app_env(Config1).
|
|
|
|
init_per_suite(Config) ->
|
|
{ok, _} = application:ensure_all_started(rabbitmq_amqp_client),
|
|
Config.
|
|
|
|
end_per_suite(Config) ->
|
|
Config.
|
|
|
|
init_per_group(all_tests_with_prefix=Group, Config0) ->
|
|
PathConfig = {rabbitmq_management, [{path_prefix, ?PATH_PREFIX}]},
|
|
Config1 = rabbit_ct_helpers:merge_app_env(Config0, PathConfig),
|
|
Config2 = finish_init(Group, Config1),
|
|
start_broker(Config2);
|
|
init_per_group(Group, Config0) ->
|
|
Config1 = finish_init(Group, Config0),
|
|
start_broker(Config1).
|
|
|
|
end_per_group(_, Config) ->
|
|
inets:stop(),
|
|
Teardown0 = rabbit_ct_client_helpers:teardown_steps(),
|
|
Teardown1 = rabbit_ct_broker_helpers:teardown_steps(),
|
|
Steps = Teardown0 ++ Teardown1,
|
|
rabbit_ct_helpers:run_teardown_steps(Config, Steps).
|
|
|
|
init_per_testcase(Testcase = permissions_vhost_test, Config) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"myvhost">>),
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"myvhost1">>),
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"myvhost2">>),
|
|
rabbit_ct_helpers:testcase_started(Config, Testcase);
|
|
init_per_testcase(Testcase = stream_queues_have_consumers_field, Config) ->
|
|
case rabbit_ct_helpers:is_mixed_versions() of
|
|
true ->
|
|
{skip, "mixed version clusters are not supported"};
|
|
_ ->
|
|
rabbit_ct_helpers:testcase_started(Config, Testcase)
|
|
end;
|
|
init_per_testcase(Testcase = disabled_operator_policy_test, Config) ->
|
|
Restrictions = [{operator_policy_changes, [{disabled, true}]}],
|
|
rabbit_ct_broker_helpers:rpc_all(Config,
|
|
application, set_env, [rabbitmq_management, restrictions, Restrictions]),
|
|
rabbit_ct_helpers:testcase_started(Config, Testcase);
|
|
init_per_testcase(Testcase = disabled_qq_replica_opers_test, Config) ->
|
|
Restrictions = [{quorum_queue_replica_operations, [{disabled, true}]}],
|
|
rabbit_ct_broker_helpers:rpc_all(Config,
|
|
application, set_env, [rabbitmq_management, restrictions, Restrictions]),
|
|
rabbit_ct_helpers:testcase_started(Config, Testcase);
|
|
init_per_testcase(Testcase = cluster_and_node_tags_test, Config) ->
|
|
Tags = [{<<"az">>, <<"us-east-3">>}, {<<"region">>,<<"us-east">>}, {<<"environment">>,<<"production">>}],
|
|
rpc(Config,
|
|
application, set_env, [rabbit, node_tags, Tags]),
|
|
rpc(
|
|
Config, rabbit_runtime_parameters, set_global,
|
|
[cluster_tags, Tags, none]),
|
|
rabbit_ct_helpers:testcase_started(Config, Testcase);
|
|
init_per_testcase(queues_detailed_test, Config) ->
|
|
IsEnabled = rabbit_ct_broker_helpers:is_feature_flag_enabled(
|
|
Config, detailed_queues_endpoint),
|
|
case IsEnabled of
|
|
true -> Config;
|
|
false -> {skip, "The detailed queues endpoint is not available."}
|
|
end;
|
|
init_per_testcase(Testcase, Config) ->
|
|
rabbit_ct_broker_helpers:close_all_connections(Config, 0, <<"rabbit_mgmt_SUITE:init_per_testcase">>),
|
|
rabbit_ct_helpers:testcase_started(Config, Testcase).
|
|
|
|
end_per_testcase(Testcase, Config) ->
|
|
rabbit_ct_broker_helpers:close_all_connections(Config, 0, <<"rabbit_mgmt_SUITE:end_per_testcase">>),
|
|
rpc(Config, application, set_env, [rabbitmq_management, disable_basic_auth, false]),
|
|
Config1 = end_per_testcase0(Testcase, Config),
|
|
rabbit_ct_helpers:testcase_finished(Config1, Testcase).
|
|
|
|
end_per_testcase0(T, Config)
|
|
when T =:= long_definitions_test; T =:= long_definitions_multipart_test ->
|
|
Vhosts = long_definitions_vhosts(T),
|
|
[rabbit_ct_broker_helpers:delete_vhost(Config, Name)
|
|
|| #{name := Name} <- Vhosts],
|
|
Config;
|
|
end_per_testcase0(definitions_password_test, Config) ->
|
|
rpc(Config, application, unset_env, [rabbit, password_hashing_module]),
|
|
Config;
|
|
end_per_testcase0(queues_test, Config) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"downvhost">>),
|
|
Config;
|
|
end_per_testcase0(vhost_limits_list_test, Config) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"limit_test_vhost_1">>),
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"limit_test_vhost_2">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"limit_test_vhost_1_user">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"limit_test_vhost_2_user">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"no_vhost_user">>),
|
|
Config;
|
|
end_per_testcase0(vhost_limit_set_test, Config) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"limit_test_vhost_1">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"limit_test_vhost_1_user">>),
|
|
Config;
|
|
end_per_testcase0(user_limits_list_test, Config) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"limit_test_vhost_1">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"limit_test_user_1_user">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"limit_test_user_2_user">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"no_vhost_user">>),
|
|
Config;
|
|
end_per_testcase0(user_limit_set_test, Config) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"limit_test_vhost_1">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"limit_test_user_1">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"limit_test_vhost_1_user">>),
|
|
Config;
|
|
end_per_testcase0(permissions_vhost_test, Config) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"myvhost1">>),
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, <<"myvhost2">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"myuser1">>),
|
|
rabbit_ct_broker_helpers:delete_user(Config, <<"myuser2">>),
|
|
Config;
|
|
end_per_testcase0(config_environment_test, Config) ->
|
|
rpc(Config, application, unset_env, [rabbit, config_environment_test_env]),
|
|
Config;
|
|
end_per_testcase0(disabled_operator_policy_test, Config) ->
|
|
rpc(Config, application, unset_env, [rabbitmq_management, restrictions]),
|
|
Config;
|
|
end_per_testcase0(disabled_qq_replica_opers_test, Config) ->
|
|
rpc(Config, application, unset_env, [rabbitmq_management, restrictions]),
|
|
Config;
|
|
end_per_testcase0(cluster_and_node_tags_test, Config) ->
|
|
rpc(
|
|
Config, application, unset_env, [rabbit, node_tags]),
|
|
rpc(
|
|
Config, rabbit_runtime_parameters, clear_global,
|
|
[cluster_tags, none]),
|
|
Config;
|
|
end_per_testcase0(Testcase, Config)
|
|
when Testcase == list_deprecated_features_test;
|
|
Testcase == list_used_deprecated_features_test ->
|
|
ok = rpc(Config, rabbit_feature_flags, clear_injected_test_feature_flags, []),
|
|
Config;
|
|
end_per_testcase0(_, Config) -> Config.
|
|
|
|
%% -------------------------------------------------------------------
|
|
%% Testcases.
|
|
%% -------------------------------------------------------------------
|
|
|
|
overview_test(Config) ->
|
|
%% Rather crude, but this req doesn't say much and at least this means it
|
|
%% didn't blow up.
|
|
true = 0 < length(maps:get(listeners, http_get(Config, "/overview"))),
|
|
http_put(Config, "/users/myuser", [{password, <<"myuser">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_get(Config, "/overview", "myuser", "myuser", ?OK),
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
passed.
|
|
|
|
cluster_name_test(Config) ->
|
|
http_put(Config, "/users/myuser", [{password, <<"myuser">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_put(Config, "/cluster-name", [{name, "foo"}], "myuser", "myuser", ?NOT_AUTHORISED),
|
|
http_put(Config, "/cluster-name", [{name, "foo"}], {group, '2xx'}),
|
|
#{name := <<"foo">>} = http_get(Config, "/cluster-name", "myuser", "myuser", ?OK),
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
passed.
|
|
|
|
nodes_test(Config) ->
|
|
http_put(Config, "/users/user", [{password, <<"user">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/monitor", [{password, <<"monitor">>},
|
|
{tags, <<"monitoring">>}], {group, '2xx'}),
|
|
DiscNode = #{type => <<"disc">>, running => true},
|
|
assert_list([DiscNode], http_get(Config, "/nodes")),
|
|
assert_list([DiscNode], http_get(Config, "/nodes", "monitor", "monitor", ?OK)),
|
|
http_get(Config, "/nodes", "user", "user", ?NOT_AUTHORISED),
|
|
http_get(Config, "/nodes/does-not-exist", ?NOT_FOUND),
|
|
[Node] = http_get(Config, "/nodes"),
|
|
Path = "/nodes/" ++ binary_to_list(maps:get(name, Node)),
|
|
assert_item(DiscNode, http_get(Config, Path, ?OK)),
|
|
assert_item(DiscNode, http_get(Config, Path, "monitor", "monitor", ?OK)),
|
|
http_get(Config, Path, "user", "user", ?NOT_AUTHORISED),
|
|
http_delete(Config, "/users/user", {group, '2xx'}),
|
|
http_delete(Config, "/users/monitor", {group, '2xx'}),
|
|
passed.
|
|
|
|
memory_test(Config) ->
|
|
[Node] = http_get(Config, "/nodes"),
|
|
Path = "/nodes/" ++ binary_to_list(maps:get(name, Node)) ++ "/memory",
|
|
Result = http_get(Config, Path, ?OK),
|
|
assert_keys([memory], Result),
|
|
Keys = [total, connection_readers, connection_writers, connection_channels,
|
|
connection_other, queue_procs, plugins,
|
|
other_proc, mnesia, mgmt_db, msg_index, other_ets, binary, code,
|
|
atom, other_system, allocated_unused, reserved_unallocated],
|
|
assert_keys(Keys, maps:get(memory, Result)),
|
|
http_get(Config, "/nodes/nonode/memory", ?NOT_FOUND),
|
|
%% Relative memory as a percentage of the total
|
|
Result1 = http_get(Config, Path ++ "/relative", ?OK),
|
|
assert_keys([memory], Result1),
|
|
Breakdown = maps:get(memory, Result1),
|
|
assert_keys(Keys, Breakdown),
|
|
|
|
assert_item(#{total => 100}, Breakdown),
|
|
%% allocated_unused and reserved_unallocated
|
|
%% make this test pretty unpredictable
|
|
assert_percentage(Breakdown, 20),
|
|
http_get(Config, "/nodes/nonode/memory/relative", ?NOT_FOUND),
|
|
passed.
|
|
|
|
ets_tables_memory_test(Config) ->
|
|
[Node] = http_get(Config, "/nodes"),
|
|
Path = "/nodes/" ++ binary_to_list(maps:get(name, Node)) ++ "/memory/ets",
|
|
Result = http_get(Config, Path, ?OK),
|
|
assert_keys([ets_tables_memory], Result),
|
|
NonMgmtKeys = [tracked_connection, tracked_channel],
|
|
Keys = [queue_stats, vhost_stats_coarse_conn_stats,
|
|
connection_created_stats, channel_process_stats, consumer_stats,
|
|
queue_msg_rates],
|
|
assert_keys(Keys ++ NonMgmtKeys, maps:get(ets_tables_memory, Result)),
|
|
http_get(Config, "/nodes/nonode/memory/ets", ?NOT_FOUND),
|
|
%% Relative memory as a percentage of the total
|
|
ResultRelative = http_get(Config, Path ++ "/relative", ?OK),
|
|
assert_keys([ets_tables_memory], ResultRelative),
|
|
Breakdown = maps:get(ets_tables_memory, ResultRelative),
|
|
assert_keys(Keys, Breakdown),
|
|
assert_item(#{total => 100}, Breakdown),
|
|
assert_percentage(Breakdown),
|
|
http_get(Config, "/nodes/nonode/memory/ets/relative", ?NOT_FOUND),
|
|
|
|
ResultMgmt = http_get(Config, Path ++ "/management", ?OK),
|
|
assert_keys([ets_tables_memory], ResultMgmt),
|
|
assert_keys(Keys, maps:get(ets_tables_memory, ResultMgmt)),
|
|
assert_no_keys(NonMgmtKeys, maps:get(ets_tables_memory, ResultMgmt)),
|
|
|
|
ResultMgmtRelative = http_get(Config, Path ++ "/management/relative", ?OK),
|
|
assert_keys([ets_tables_memory], ResultMgmtRelative),
|
|
assert_keys(Keys, maps:get(ets_tables_memory, ResultMgmtRelative)),
|
|
assert_no_keys(NonMgmtKeys, maps:get(ets_tables_memory, ResultMgmtRelative)),
|
|
assert_item(#{total => 100}, maps:get(ets_tables_memory, ResultMgmtRelative)),
|
|
assert_percentage(maps:get(ets_tables_memory, ResultMgmtRelative)),
|
|
|
|
ResultUnknownFilter = http_get(Config, Path ++ "/blahblah", ?OK),
|
|
#{ets_tables_memory := <<"no_tables">>} = ResultUnknownFilter,
|
|
passed.
|
|
|
|
assert_percentage(Breakdown0) ->
|
|
assert_percentage(Breakdown0, 0).
|
|
|
|
assert_percentage(Breakdown0, ExtraMargin) ->
|
|
Breakdown = maps:to_list(Breakdown0),
|
|
Total = lists:sum([P || {K, P} <- Breakdown, K =/= total]),
|
|
AcceptableMargin = (length(Breakdown) - 1) + ExtraMargin,
|
|
%% Rounding up and down can lose some digits. Never more than the number
|
|
%% of items in the breakdown.
|
|
case ((Total =< 100 + AcceptableMargin) andalso (Total >= 100 - AcceptableMargin)) of
|
|
false ->
|
|
throw({bad_percentage, Total, Breakdown});
|
|
true ->
|
|
ok
|
|
end.
|
|
|
|
println(What) -> io:format("~tp~n", [What]).
|
|
|
|
auth_test(Config) ->
|
|
http_put(Config, "/users/user", [{password, <<"user">>},
|
|
{tags, <<"">>}], {group, '2xx'}),
|
|
EmptyAuthResponseHeaders = test_auth(Config, ?NOT_AUTHORISED, []),
|
|
?assertEqual(true, lists:keymember("www-authenticate", 1, EmptyAuthResponseHeaders)),
|
|
%% NOTE: this one won't have www-authenticate in the response,
|
|
%% because user/password are ok, tags are not
|
|
test_auth(Config, ?NOT_AUTHORISED, [auth_header("user", "user")]),
|
|
%?assertEqual(true, lists:keymember("www-authenticate", 1, WrongAuthResponseHeaders)),
|
|
test_auth(Config, ?OK, [auth_header("guest", "guest")]),
|
|
http_delete(Config, "/users/user", {group, '2xx'}),
|
|
passed.
|
|
|
|
%% This test is rather over-verbose as we're trying to test understanding of
|
|
%% Webmachine
|
|
vhosts_test(Config) ->
|
|
assert_list([#{name => <<"/">>}], http_get(Config, "/vhosts")),
|
|
%% Create a new one
|
|
http_put(Config, "/vhosts/myvhost", none, {group, '2xx'}),
|
|
%% PUT should be idempotent
|
|
http_put(Config, "/vhosts/myvhost", none, {group, '2xx'}),
|
|
%% Check it's there
|
|
assert_list([#{name => <<"/">>}, #{name => <<"myvhost">>}],
|
|
http_get(Config, "/vhosts")),
|
|
%% Check individually
|
|
assert_item(#{name => <<"/">>}, http_get(Config, "/vhosts/%2F", ?OK)),
|
|
assert_item(#{name => <<"myvhost">>},http_get(Config, "/vhosts/myvhost")),
|
|
|
|
%% Crash it
|
|
rabbit_ct_broker_helpers:force_vhost_failure(Config, <<"myvhost">>),
|
|
[NodeData] = http_get(Config, "/nodes"),
|
|
Node = binary_to_atom(maps:get(name, NodeData), utf8),
|
|
assert_item(#{name => <<"myvhost">>, cluster_state => #{Node => <<"stopped">>}},
|
|
http_get(Config, "/vhosts/myvhost")),
|
|
|
|
%% Restart it
|
|
http_post(Config, "/vhosts/myvhost/start/" ++ atom_to_list(Node), [], {group, '2xx'}),
|
|
assert_item(#{name => <<"myvhost">>, cluster_state => #{Node => <<"running">>}},
|
|
http_get(Config, "/vhosts/myvhost")),
|
|
|
|
%% Delete it
|
|
http_delete(Config, "/vhosts/myvhost", {group, '2xx'}),
|
|
%% It's not there
|
|
http_get(Config, "/vhosts/myvhost", ?NOT_FOUND),
|
|
http_delete(Config, "/vhosts/myvhost", ?NOT_FOUND),
|
|
|
|
passed.
|
|
|
|
vhosts_description_test(Config) ->
|
|
http_put(Config, "/vhosts/myvhost", [{description, <<"vhost description">>},
|
|
{tags, <<"tag1,tag2">>}], {group, '2xx'}),
|
|
Expected = #{name => <<"myvhost">>,
|
|
metadata => #{
|
|
description => <<"vhost description">>,
|
|
%% this is an injected DQT
|
|
default_queue_type => <<"classic">>,
|
|
tags => [<<"tag1">>, <<"tag2">>]
|
|
}},
|
|
assert_item(Expected, http_get(Config, "/vhosts/myvhost")),
|
|
|
|
%% Delete it
|
|
http_delete(Config, "/vhosts/myvhost", {group, '2xx'}),
|
|
|
|
passed.
|
|
|
|
vhosts_trace_test(Config) ->
|
|
http_put(Config, "/vhosts/myvhost", none, {group, '2xx'}),
|
|
Disabled = #{name => <<"myvhost">>, tracing => false},
|
|
Enabled = #{name => <<"myvhost">>, tracing => true},
|
|
assert_item(Disabled, http_get(Config, "/vhosts/myvhost")),
|
|
http_put(Config, "/vhosts/myvhost", [{tracing, true}], {group, '2xx'}),
|
|
assert_item(Enabled, http_get(Config, "/vhosts/myvhost")),
|
|
http_put(Config, "/vhosts/myvhost", [{tracing, true}], {group, '2xx'}),
|
|
assert_item(Enabled, http_get(Config, "/vhosts/myvhost")),
|
|
http_put(Config, "/vhosts/myvhost", [{tracing, false}], {group, '2xx'}),
|
|
assert_item(Disabled, http_get(Config, "/vhosts/myvhost")),
|
|
http_delete(Config, "/vhosts/myvhost", {group, '2xx'}),
|
|
|
|
passed.
|
|
|
|
users_test(Config) ->
|
|
assert_item(#{name => <<"guest">>, tags => [<<"administrator">>]},
|
|
http_get(Config, "/whoami")),
|
|
rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env,
|
|
[rabbitmq_management, login_session_timeout, 100]),
|
|
assert_item(#{name => <<"guest">>,
|
|
tags => [<<"administrator">>],
|
|
login_session_timeout => 100},
|
|
http_get(Config, "/whoami")),
|
|
http_delete(Config, "/users/users_test", [?NO_CONTENT, ?NOT_FOUND]),
|
|
http_get(Config, "/users/users_test", [?NO_CONTENT, ?NOT_FOUND]),
|
|
http_put_raw(Config, "/users/users_test", "Something not JSON", ?BAD_REQUEST),
|
|
http_put(Config, "/users/users_test", [{flim, <<"flam">>}], ?BAD_REQUEST),
|
|
http_put(Config, "/users/users_test", [{tags, [<<"management">>]},
|
|
{password, <<"users_test">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/users/users_test", [{password_hash, <<"not_hash">>}], ?BAD_REQUEST),
|
|
http_put(Config, "/users/users_test", [{password_hash,
|
|
<<"IECV6PZI/Invh0DL187KFpkO5Jc=">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
assert_item(#{name => <<"users_test">>, tags => [<<"management">>],
|
|
password_hash => <<"IECV6PZI/Invh0DL187KFpkO5Jc=">>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha256">>},
|
|
http_get(Config, "/users/users_test")),
|
|
|
|
http_put(Config, "/users/users_test", [{password_hash,
|
|
<<"IECV6PZI/Invh0DL187KFpkO5Jc=">>},
|
|
{hashing_algorithm, <<"rabbit_password_hashing_md5">>},
|
|
{tags, [<<"management">>]}], {group, '2xx'}),
|
|
assert_item(#{name => <<"users_test">>, tags => [<<"management">>],
|
|
password_hash => <<"IECV6PZI/Invh0DL187KFpkO5Jc=">>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_md5">>},
|
|
http_get(Config, "/users/users_test")),
|
|
http_put(Config, "/users/users_test", [{password, <<"password">>},
|
|
{tags, [<<"administrator">>, <<"foo">>]}], {group, '2xx'}),
|
|
assert_item(#{name => <<"users_test">>, tags => [<<"administrator">>, <<"foo">>]},
|
|
http_get(Config, "/users/users_test")),
|
|
Listed = lists:sort(http_get(Config, "/users")),
|
|
ct:pal("Listed users: ~tp", [Listed]),
|
|
User1 = #{name => <<"users_test">>, tags => [<<"administrator">>, <<"foo">>]},
|
|
User2 = #{name => <<"guest">>, tags => [<<"administrator">>]},
|
|
?assert(lists:any(fun(U) ->
|
|
maps:get(name, U) =:= maps:get(name, User1) andalso
|
|
maps:get(tags, U) =:= maps:get(tags, User1)
|
|
end, Listed)),
|
|
?assert(lists:any(fun(U) ->
|
|
maps:get(name, U) =:= maps:get(name, User2) andalso
|
|
maps:get(tags, U) =:= maps:get(tags, User2)
|
|
end, Listed)),
|
|
test_auth(Config, ?OK, [auth_header("users_test", "password")]),
|
|
http_put(Config, "/users/users_test", [{password, <<"password">>},
|
|
{tags, []}], {group, '2xx'}),
|
|
assert_item(#{name => <<"users_test">>, tags => []},
|
|
http_get(Config, "/users/users_test")),
|
|
http_delete(Config, "/users/users_test", {group, '2xx'}),
|
|
test_auth(Config, ?NOT_AUTHORISED, [auth_header("users_test", "password")]),
|
|
http_get(Config, "/users/users_test", ?NOT_FOUND),
|
|
passed.
|
|
|
|
users_protected_test(Config) ->
|
|
ProtectedUser = <<"protected_user">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, ProtectedUser),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, ProtectedUser, [management, protected]),
|
|
|
|
%% Verify that a protected user cannot be updated via the HTTP API
|
|
http_put(Config, "/users/protected_user", [{password, <<"new_password">>},
|
|
{tags, <<"management,protected">>}], ?BAD_REQUEST),
|
|
|
|
%% Verify that a protected user cannot be deleted via the HTTP API
|
|
http_delete(Config, "/users/protected_user", ?BAD_REQUEST),
|
|
|
|
rabbit_ct_broker_helpers:delete_user(Config, ProtectedUser),
|
|
passed.
|
|
|
|
without_permissions_users_test(Config) ->
|
|
assert_item(#{name => <<"guest">>, tags => [<<"administrator">>]},
|
|
http_get(Config, "/whoami")),
|
|
http_put(Config, "/users/myuser", [{password_hash,
|
|
<<"IECV6PZI/Invh0DL187KFpkO5Jc=">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
Perms = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/permissions/%2F/myuser", Perms, {group, '2xx'}),
|
|
http_put(Config, "/users/myuserwithoutpermissions", [{password_hash,
|
|
<<"IECV6PZI/Invh0DL187KFpkO5Jc=">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
assert_list([#{name => <<"myuserwithoutpermissions">>, tags => [<<"management">>],
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha256">>,
|
|
password_hash => <<"IECV6PZI/Invh0DL187KFpkO5Jc=">>}],
|
|
http_get(Config, "/users/without-permissions")),
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuserwithoutpermissions", {group, '2xx'}),
|
|
passed.
|
|
|
|
users_bulk_delete_test(Config) ->
|
|
assert_item(#{name => <<"guest">>, tags => [<<"administrator">>]},
|
|
http_get(Config, "/whoami")),
|
|
http_put(Config, "/users/myuser1", [{tags, <<"management">>}, {password, <<"myuser">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/users/myuser2", [{tags, <<"management">>}, {password, <<"myuser">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/users/myuser3", [{tags, <<"management">>}, {password, <<"myuser">>}],
|
|
{group, '2xx'}),
|
|
http_get(Config, "/users/myuser1", {group, '2xx'}),
|
|
http_get(Config, "/users/myuser2", {group, '2xx'}),
|
|
http_get(Config, "/users/myuser3", {group, '2xx'}),
|
|
|
|
http_post_json(Config, "/users/bulk-delete",
|
|
"{\"users\": [\"myuser1\", \"myuser2\"]}", {group, '2xx'}),
|
|
http_get(Config, "/users/myuser1", ?NOT_FOUND),
|
|
http_get(Config, "/users/myuser2", ?NOT_FOUND),
|
|
http_get(Config, "/users/myuser3", {group, '2xx'}),
|
|
http_post_json(Config, "/users/bulk-delete", "{\"users\": [\"myuser3\"]}",
|
|
{group, '2xx'}),
|
|
http_get(Config, "/users/myuser3", ?NOT_FOUND),
|
|
passed.
|
|
|
|
users_legacy_administrator_test(Config) ->
|
|
http_put(Config, "/users/myuser1", [{administrator, <<"true">>},
|
|
{password, <<"myuser1">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/users/myuser2", [{administrator, <<"false">>},
|
|
{password, <<"myuser2">>}],
|
|
{group, '2xx'}),
|
|
assert_item(#{name => <<"myuser1">>, tags => [<<"administrator">>]},
|
|
http_get(Config, "/users/myuser1")),
|
|
assert_item(#{name => <<"myuser2">>, tags => []},
|
|
http_get(Config, "/users/myuser2")),
|
|
http_delete(Config, "/users/myuser1", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuser2", {group, '2xx'}),
|
|
passed.
|
|
|
|
adding_a_user_with_password_test(Config) ->
|
|
http_put(Config, "/users/user10", [{tags, <<"management">>},
|
|
{password, <<"password">>}],
|
|
[?CREATED, ?NO_CONTENT]),
|
|
http_delete(Config, "/users/user10", ?NO_CONTENT).
|
|
adding_a_user_with_password_hash_test(Config) ->
|
|
http_put(Config, "/users/user11", [{tags, <<"management">>},
|
|
%% SHA-256 of "secret"
|
|
{password_hash, <<"2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b">>}],
|
|
[?CREATED, ?NO_CONTENT]),
|
|
http_delete(Config, "/users/user11", ?NO_CONTENT).
|
|
|
|
adding_a_user_with_generated_password_hash_test(Config) ->
|
|
#{ok := HashedPassword} = http_get(Config, "/auth/hash_password/some_password"),
|
|
|
|
http_put(Config, "/users/user12", [{tags, <<"administrator">>},
|
|
{password_hash, HashedPassword}],
|
|
[?CREATED, ?NO_CONTENT]),
|
|
% If the get succeeded, the hashed password generation is correct
|
|
User = http_get(Config, "/users/user12", "user12", "some_password", ?OK),
|
|
?assertEqual(maps:get(password_hash, User), HashedPassword),
|
|
http_delete(Config, "/users/user12", ?NO_CONTENT).
|
|
|
|
adding_a_user_with_permissions_in_single_operation_test(Config) ->
|
|
QArgs = #{},
|
|
PermArgs = #{configure => <<".*">>,
|
|
write => <<".*">>,
|
|
read => <<".*">>},
|
|
http_delete(Config, "/vhosts/vhost42", [?NO_CONTENT, ?NOT_FOUND]),
|
|
http_delete(Config, "/vhosts/vhost43", [?NO_CONTENT, ?NOT_FOUND]),
|
|
http_delete(Config, "/users/user-preconfigured-perms", [?NO_CONTENT, ?NOT_FOUND]),
|
|
|
|
http_put(Config, "/vhosts/vhost42", none, [?CREATED, ?NO_CONTENT]),
|
|
http_put(Config, "/vhosts/vhost43", none, [?CREATED, ?NO_CONTENT]),
|
|
|
|
http_put(Config, "/users/user-preconfigured-perms", [{password, <<"user-preconfigured-perms">>},
|
|
{tags, <<"management">>},
|
|
{permissions, [
|
|
{<<"vhost42">>, PermArgs},
|
|
{<<"vhost43">>, PermArgs}
|
|
]}],
|
|
[?CREATED, ?NO_CONTENT]),
|
|
assert_list([#{tracing => false, name => <<"vhost42">>},
|
|
#{tracing => false, name => <<"vhost43">>}],
|
|
http_get(Config, "/vhosts", "user-preconfigured-perms", "user-preconfigured-perms", ?OK)),
|
|
http_put(Config, "/queues/vhost42/myqueue", QArgs,
|
|
"user-preconfigured-perms", "user-preconfigured-perms", [?CREATED, ?NO_CONTENT]),
|
|
http_put(Config, "/queues/vhost43/myqueue", QArgs,
|
|
"user-preconfigured-perms", "user-preconfigured-perms", [?CREATED, ?NO_CONTENT]),
|
|
Test1 =
|
|
fun(Path) ->
|
|
http_get(Config, Path, "user-preconfigured-perms", "user-preconfigured-perms", ?OK)
|
|
end,
|
|
Test2 =
|
|
fun(Path1, Path2) ->
|
|
http_get(Config, Path1 ++ "/vhost42/" ++ Path2, "user-preconfigured-perms", "user-preconfigured-perms",
|
|
?OK),
|
|
http_get(Config, Path1 ++ "/vhost43/" ++ Path2, "user-preconfigured-perms", "user-preconfigured-perms",
|
|
?OK)
|
|
end,
|
|
Test1("/exchanges"),
|
|
Test2("/exchanges", ""),
|
|
Test2("/exchanges", "amq.direct"),
|
|
Test1("/queues"),
|
|
Test2("/queues", ""),
|
|
Test2("/queues", "myqueue"),
|
|
Test1("/bindings"),
|
|
Test2("/bindings", ""),
|
|
Test2("/queues", "myqueue/bindings"),
|
|
Test2("/exchanges", "amq.default/bindings/source"),
|
|
Test2("/exchanges", "amq.default/bindings/destination"),
|
|
Test2("/bindings", "e/amq.default/q/myqueue"),
|
|
Test2("/bindings", "e/amq.default/q/myqueue/myqueue"),
|
|
http_delete(Config, "/vhosts/vhost42", ?NO_CONTENT),
|
|
http_delete(Config, "/vhosts/vhost43", ?NO_CONTENT),
|
|
http_delete(Config, "/users/user-preconfigured-perms", ?NO_CONTENT),
|
|
passed.
|
|
|
|
adding_a_user_without_tags_fails_test(Config) ->
|
|
http_put(Config, "/users/no-tags", [{password, <<"password">>}], ?BAD_REQUEST).
|
|
|
|
%% creating a passwordless user makes sense when x509x certificates or another
|
|
%% "external" authentication mechanism or backend is used.
|
|
%% See rabbitmq/rabbitmq-management#383.
|
|
adding_a_user_without_password_or_hash_test(Config) ->
|
|
http_put(Config, "/users/no-pwd", [{tags, <<"management">>}], [?CREATED, ?NO_CONTENT]),
|
|
http_put(Config, "/users/no-pwd", [{tags, <<"management">>}], [?CREATED, ?NO_CONTENT]),
|
|
http_delete(Config, "/users/no-pwd", ?NO_CONTENT).
|
|
|
|
adding_a_user_with_both_password_and_hash_fails_test(Config) ->
|
|
http_put(Config, "/users/myuser", [{password, <<"password">>},
|
|
{password_hash, <<"password_hash">>}],
|
|
?BAD_REQUEST),
|
|
http_put(Config, "/users/myuser", [{tags, <<"management">>},
|
|
{password, <<"password">>},
|
|
{password_hash, <<"password_hash">>}], ?BAD_REQUEST).
|
|
|
|
updating_a_user_without_password_or_hash_clears_password_test(Config) ->
|
|
http_put(Config, "/users/myuser", [{tags, <<"management">>},
|
|
{password, <<"myuser">>}], [?CREATED, ?NO_CONTENT]),
|
|
%% in this case providing no password or password_hash will
|
|
%% clear users' credentials
|
|
http_put(Config, "/users/myuser", [{tags, <<"management">>}], [?CREATED, ?NO_CONTENT]),
|
|
assert_item(#{name => <<"myuser">>,
|
|
tags => [<<"management">>],
|
|
password_hash => <<>>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha256">>},
|
|
http_get(Config, "/users/myuser")),
|
|
http_delete(Config, "/users/myuser", ?NO_CONTENT).
|
|
|
|
-define(NON_GUEST_USERNAME, <<"abc">>).
|
|
|
|
user_credential_validation_accept_everything_succeeds_test(Config) ->
|
|
rabbit_ct_broker_helpers:delete_user(Config, ?NON_GUEST_USERNAME),
|
|
rabbit_ct_broker_helpers:switch_credential_validator(Config, accept_everything),
|
|
http_put(Config, "/users/abc", [{password, <<"password">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
rabbit_ct_broker_helpers:delete_user(Config, ?NON_GUEST_USERNAME).
|
|
|
|
user_credential_validation_min_length_succeeds_test(Config) ->
|
|
rabbit_ct_broker_helpers:delete_user(Config, ?NON_GUEST_USERNAME),
|
|
rabbit_ct_broker_helpers:switch_credential_validator(Config, min_length, 5),
|
|
http_put(Config, "/users/abc", [{password, <<"password">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
rabbit_ct_broker_helpers:delete_user(Config, ?NON_GUEST_USERNAME),
|
|
rabbit_ct_broker_helpers:switch_credential_validator(Config, accept_everything).
|
|
|
|
user_credential_validation_min_length_fails_test(Config) ->
|
|
rabbit_ct_broker_helpers:delete_user(Config, ?NON_GUEST_USERNAME),
|
|
rabbit_ct_broker_helpers:switch_credential_validator(Config, min_length, 5),
|
|
http_put(Config, "/users/abc", [{password, <<"_">>},
|
|
{tags, <<"management">>}], ?BAD_REQUEST),
|
|
rabbit_ct_broker_helpers:switch_credential_validator(Config, accept_everything).
|
|
|
|
updating_tags_of_a_passwordless_user_test(Config) ->
|
|
rabbit_ct_broker_helpers:delete_user(Config, ?NON_GUEST_USERNAME),
|
|
http_put(Config, "/users/abc", [{tags, <<"management">>},
|
|
{password, <<"myuser">>}], [?CREATED, ?NO_CONTENT]),
|
|
|
|
%% clear user's password
|
|
http_put(Config, "/users/abc", [{tags, <<"management">>}], [?CREATED, ?NO_CONTENT]),
|
|
assert_item(#{name => ?NON_GUEST_USERNAME,
|
|
tags => [<<"management">>],
|
|
password_hash => <<>>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha256">>},
|
|
http_get(Config, "/users/abc")),
|
|
|
|
http_put(Config, "/users/abc", [{tags, <<"impersonator">>}], [?CREATED, ?NO_CONTENT]),
|
|
assert_item(#{name => ?NON_GUEST_USERNAME,
|
|
tags => [<<"impersonator">>],
|
|
password_hash => <<>>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha256">>},
|
|
http_get(Config, "/users/abc")),
|
|
|
|
http_put(Config, "/users/abc", [{tags, <<"">>}], [?CREATED, ?NO_CONTENT]),
|
|
assert_item(#{name => ?NON_GUEST_USERNAME,
|
|
tags => [],
|
|
password_hash => <<>>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha256">>},
|
|
http_get(Config, "/users/abc")),
|
|
|
|
http_delete(Config, "/users/abc", ?NO_CONTENT).
|
|
|
|
permissions_validation_test(Config) ->
|
|
Good = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/permissions/wrong/guest", Good, ?BAD_REQUEST),
|
|
http_put(Config, "/permissions/%2F/wrong", Good, ?BAD_REQUEST),
|
|
http_put(Config, "/permissions/%2F/guest",
|
|
[{configure, <<"[">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
?BAD_REQUEST),
|
|
http_put(Config, "/permissions/%2F/guest", Good, {group, '2xx'}),
|
|
passed.
|
|
|
|
permissions_list_test(Config) ->
|
|
AllPerms = http_get(Config, "/permissions"),
|
|
GuestInDefaultVHost = #{user => <<"guest">>,
|
|
vhost => <<"/">>,
|
|
configure => <<".*">>,
|
|
write => <<".*">>,
|
|
read => <<".*">>},
|
|
|
|
?assert(lists:member(GuestInDefaultVHost, AllPerms)),
|
|
|
|
http_put(Config, "/users/myuser1", [{password, <<"">>}, {tags, <<"administrator">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/users/myuser2", [{password, <<"">>}, {tags, <<"administrator">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost1", none, {group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost2", none, {group, '2xx'}),
|
|
|
|
Perms = [{configure, <<"foo">>}, {write, <<"foo">>}, {read, <<"foo">>}],
|
|
http_put(Config, "/permissions/myvhost1/myuser1", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/myvhost2/myuser1", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/myvhost1/myuser2", Perms, {group, '2xx'}),
|
|
|
|
%% The user that creates the vhosts gets permission automatically
|
|
%% See https://github.com/rabbitmq/rabbitmq-management/issues/444
|
|
?assertEqual(6, length(http_get(Config, "/permissions"))),
|
|
?assertEqual(2, length(http_get(Config, "/users/myuser1/permissions"))),
|
|
?assertEqual(1, length(http_get(Config, "/users/myuser2/permissions"))),
|
|
|
|
http_get(Config, "/users/notmyuser/permissions", ?NOT_FOUND),
|
|
http_get(Config, "/vhosts/notmyvhost/permissions", ?NOT_FOUND),
|
|
|
|
http_delete(Config, "/users/myuser1", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuser2", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/myvhost1", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/myvhost2", {group, '2xx'}),
|
|
passed.
|
|
|
|
permissions_test(Config) ->
|
|
http_put(Config, "/users/myuser", [{password, <<"myuser">>}, {tags, <<"administrator">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost", none, {group, '2xx'}),
|
|
|
|
http_put(Config, "/permissions/myvhost/myuser",
|
|
[{configure, <<"foo">>}, {write, <<"foo">>}, {read, <<"foo">>}],
|
|
{group, '2xx'}),
|
|
|
|
Permission = #{user => <<"myuser">>,
|
|
vhost => <<"myvhost">>,
|
|
configure => <<"foo">>,
|
|
write => <<"foo">>,
|
|
read => <<"foo">>},
|
|
%% The user that creates the vhosts gets permission automatically
|
|
%% See https://github.com/rabbitmq/rabbitmq-management/issues/444
|
|
PermissionOwner = #{user => <<"guest">>,
|
|
vhost => <<"/">>,
|
|
configure => <<".*">>,
|
|
write => <<".*">>,
|
|
read => <<".*">>},
|
|
Default = #{user => <<"guest">>,
|
|
vhost => <<"/">>,
|
|
configure => <<".*">>,
|
|
write => <<".*">>,
|
|
read => <<".*">>},
|
|
Permission = http_get(Config, "/permissions/myvhost/myuser"),
|
|
assert_list(lists:sort([Permission, PermissionOwner, Default]),
|
|
lists:sort(http_get(Config, "/permissions"))),
|
|
assert_list([Permission], http_get(Config, "/users/myuser/permissions")),
|
|
http_delete(Config, "/permissions/myvhost/myuser", {group, '2xx'}),
|
|
http_get(Config, "/permissions/myvhost/myuser", ?NOT_FOUND),
|
|
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/myvhost", {group, '2xx'}),
|
|
passed.
|
|
|
|
topic_permissions_list_test(Config) ->
|
|
http_put(Config, "/users/myuser1", [{password, <<"">>}, {tags, <<"administrator">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/users/myuser2", [{password, <<"">>}, {tags, <<"administrator">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost1", none, {group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost2", none, {group, '2xx'}),
|
|
|
|
TopicPerms = [{exchange, <<"amq.topic">>}, {write, <<"^a">>}, {read, <<"^b">>}],
|
|
http_put(Config, "/topic-permissions/myvhost1/myuser1", TopicPerms, {group, '2xx'}),
|
|
http_put(Config, "/topic-permissions/myvhost2/myuser1", TopicPerms, {group, '2xx'}),
|
|
http_put(Config, "/topic-permissions/myvhost1/myuser2", TopicPerms, {group, '2xx'}),
|
|
|
|
TopicPerms2 = [{exchange, <<"amq.direct">>}, {write, <<"^a">>}, {read, <<"^b">>}],
|
|
http_put(Config, "/topic-permissions/myvhost1/myuser1", TopicPerms2, {group, '2xx'}),
|
|
|
|
4 = length(http_get(Config, "/topic-permissions")),
|
|
3 = length(http_get(Config, "/users/myuser1/topic-permissions")),
|
|
1 = length(http_get(Config, "/users/myuser2/topic-permissions")),
|
|
3 = length(http_get(Config, "/vhosts/myvhost1/topic-permissions")),
|
|
1 = length(http_get(Config, "/vhosts/myvhost2/topic-permissions")),
|
|
|
|
http_get(Config, "/users/notmyuser/topic-permissions", ?NOT_FOUND),
|
|
http_get(Config, "/vhosts/notmyvhost/topic-permissions", ?NOT_FOUND),
|
|
|
|
%% Delete permissions for a single vhost-user-exchange combination
|
|
http_delete(Config, "/topic-permissions/myvhost1/myuser1/amq.direct", {group, '2xx'}),
|
|
3 = length(http_get(Config, "/topic-permissions")),
|
|
|
|
http_delete(Config, "/users/myuser1", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuser2", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/myvhost1", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/myvhost2", {group, '2xx'}),
|
|
passed.
|
|
|
|
topic_permissions_test(Config) ->
|
|
http_put(Config, "/users/myuser1", [{password, <<"">>}, {tags, <<"administrator">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/users/myuser2", [{password, <<"">>}, {tags, <<"administrator">>}],
|
|
{group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost1", none, {group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost2", none, {group, '2xx'}),
|
|
|
|
TopicPerms = [{exchange, <<"amq.topic">>}, {write, <<"^a">>}, {read, <<"^b">>}],
|
|
http_put(Config, "/topic-permissions/myvhost1/myuser1", TopicPerms, {group, '2xx'}),
|
|
http_put(Config, "/topic-permissions/myvhost2/myuser1", TopicPerms, {group, '2xx'}),
|
|
http_put(Config, "/topic-permissions/myvhost1/myuser2", TopicPerms, {group, '2xx'}),
|
|
|
|
3 = length(http_get(Config, "/topic-permissions")),
|
|
1 = length(http_get(Config, "/topic-permissions/myvhost1/myuser1")),
|
|
1 = length(http_get(Config, "/topic-permissions/myvhost2/myuser1")),
|
|
1 = length(http_get(Config, "/topic-permissions/myvhost1/myuser2")),
|
|
|
|
http_get(Config, "/topic-permissions/myvhost1/notmyuser", ?NOT_FOUND),
|
|
http_get(Config, "/topic-permissions/notmyvhost/myuser2", ?NOT_FOUND),
|
|
|
|
http_delete(Config, "/users/myuser1", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuser2", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/myvhost1", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/myvhost2", {group, '2xx'}),
|
|
passed.
|
|
|
|
connections_amqpl(Config) ->
|
|
{Conn, _Ch} = open_connection_and_channel(Config),
|
|
LocalPort = local_port(Conn),
|
|
Path = binary_to_list(
|
|
rabbit_mgmt_format:print(
|
|
"/connections/127.0.0.1%3A~w%20-%3E%20127.0.0.1%3A~w",
|
|
[LocalPort, amqp_port(Config)])),
|
|
await_condition(
|
|
fun () ->
|
|
Connection = http_get(Config, Path, ?OK),
|
|
?assert(maps:is_key(recv_oct, Connection)),
|
|
?assert(maps:is_key(garbage_collection, Connection)),
|
|
?assert(maps:is_key(send_oct_details, Connection)),
|
|
?assert(maps:is_key(reductions, Connection)),
|
|
true
|
|
end),
|
|
http_delete(Config, Path, {group, '2xx'}),
|
|
%% TODO rabbit_reader:shutdown/2 returns before the connection is
|
|
%% closed. It may not be worth fixing.
|
|
Fun = fun() ->
|
|
try
|
|
http_get(Config, Path, ?NOT_FOUND),
|
|
true
|
|
catch
|
|
_:_ ->
|
|
false
|
|
end
|
|
end,
|
|
await_condition(Fun),
|
|
close_connection(Conn),
|
|
passed.
|
|
|
|
%% Test that AMQP 1.0 connection can be listed and closed via the rabbitmq_management plugin.
|
|
connections_amqp(Config) ->
|
|
Node = atom_to_binary(rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename)),
|
|
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp),
|
|
User = <<"guest">>,
|
|
OpnConf = #{address => ?config(rmq_hostname, Config),
|
|
port => Port,
|
|
container_id => <<"my container">>,
|
|
sasl => {plain, User, <<"guest">>}},
|
|
{ok, C1} = amqp10_client:open_connection(OpnConf),
|
|
receive {amqp10_event, {connection, C1, opened}} -> ok
|
|
after 5000 -> ct:fail(opened_timeout)
|
|
end,
|
|
eventually(?_assertEqual(1, length(http_get(Config, "/connections"))), 1000, 10),
|
|
?assertEqual(1, length(rpc(Config, rabbit_amqp1_0, list_local, []))),
|
|
[Connection1] = http_get(Config, "/connections"),
|
|
?assertMatch(#{node := Node,
|
|
vhost := <<"/">>,
|
|
user := User,
|
|
auth_mechanism := <<"PLAIN">>,
|
|
protocol := <<"AMQP 1-0">>,
|
|
client_properties := #{version := _,
|
|
product := <<"AMQP 1.0 client">>,
|
|
platform := _}},
|
|
Connection1),
|
|
ConnectionName = maps:get(name, Connection1),
|
|
http_delete(Config,
|
|
"/connections/" ++ binary_to_list(uri_string:quote(ConnectionName)),
|
|
?NO_CONTENT),
|
|
receive {amqp10_event,
|
|
{connection, C1,
|
|
{closed,
|
|
{internal_error,
|
|
<<"Connection forced: \"Closed via management plugin\"">>}}}} -> ok
|
|
after 5000 -> ct:fail(closed_timeout)
|
|
end,
|
|
eventually(?_assertNot(is_process_alive(C1))),
|
|
eventually(?_assertEqual([], http_get(Config, "/connections")), 10, 5),
|
|
|
|
{ok, C2} = amqp10_client:open_connection(OpnConf),
|
|
receive {amqp10_event, {connection, C2, opened}} -> ok
|
|
after 5000 -> ct:fail(opened_timeout)
|
|
end,
|
|
eventually(?_assertEqual(1, length(http_get(Config, "/connections"))), 1000, 10),
|
|
http_delete(Config,
|
|
"/connections/username/guest",
|
|
?NO_CONTENT),
|
|
receive {amqp10_event,
|
|
{connection, C2,
|
|
{closed,
|
|
{internal_error,
|
|
<<"Connection forced: \"Closed via management plugin\"">>}}}} -> ok
|
|
after 5000 -> ct:fail(closed_timeout)
|
|
end,
|
|
eventually(?_assertNot(is_process_alive(C2))),
|
|
eventually(?_assertEqual([], http_get(Config, "/connections")), 10, 5),
|
|
?assertEqual(0, length(rpc(Config, rabbit_amqp1_0, list_local, []))).
|
|
|
|
%% Test that AMQP 1.0 sessions and links can be listed via the rabbitmq_management plugin.
|
|
amqp_sessions(Config) ->
|
|
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp),
|
|
User = <<"guest">>,
|
|
OpnConf = #{address => ?config(rmq_hostname, Config),
|
|
port => Port,
|
|
container_id => <<"my container">>,
|
|
sasl => {plain, User, <<"guest">>}},
|
|
{ok, C} = amqp10_client:open_connection(OpnConf),
|
|
receive {amqp10_event, {connection, C, opened}} -> ok
|
|
after 5000 -> ct:fail(opened_timeout)
|
|
end,
|
|
|
|
{ok, Session1} = amqp10_client:begin_session_sync(C),
|
|
{ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(
|
|
Session1, <<"my link pair">>),
|
|
QName = <<"my stream">>,
|
|
QProps = #{arguments => #{<<"x-queue-type">> => {utf8, <<"stream">>}}},
|
|
{ok, #{}} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, QProps),
|
|
{ok, Sender} = amqp10_client:attach_sender_link_sync(
|
|
Session1, <<"my sender">>,
|
|
rabbitmq_amqp_address:exchange(<<"amq.direct">>, <<"my key">>)),
|
|
|
|
Filter = #{<<"ts filter">> => #filter{descriptor = <<"rabbitmq:stream-offset-spec">>,
|
|
value = {timestamp, 1751023462000}},
|
|
<<"bloom filter">> => #filter{descriptor = <<"rabbitmq:stream-filter">>,
|
|
value = {list, [{utf8, <<"complaint">>},
|
|
{utf8, <<"user1">>}]}},
|
|
<<"match filter">> => #filter{descriptor = <<"rabbitmq:stream-match-unfiltered">>,
|
|
value = {boolean, true}},
|
|
<<"prop filter">> => #filter{descriptor = ?DESCRIPTOR_CODE_PROPERTIES_FILTER,
|
|
value = {map, [{{symbol, <<"subject">>},
|
|
{utf8, <<"complaint">>}},
|
|
{{symbol, <<"user-id">>},
|
|
{binary, <<"user1">>}}
|
|
]}},
|
|
<<"app prop filter">> => #filter{descriptor = ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER,
|
|
value = {map, [{{utf8, <<"k1">>}, {int, -4}},
|
|
{{utf8, <<"☀️"/utf8>>}, {utf8, <<"🙂"/utf8>>}}
|
|
]}}},
|
|
{ok, Receiver} = amqp10_client:attach_receiver_link(
|
|
Session1, <<"my receiver">>,
|
|
rabbitmq_amqp_address:queue(QName),
|
|
settled, none, Filter),
|
|
receive {amqp10_event, {link, Receiver, attached}} -> ok
|
|
after 5000 -> ct:fail({missing_event, ?LINE})
|
|
end,
|
|
ok = amqp10_client:flow_link_credit(Receiver, 5000, never),
|
|
|
|
eventually(?_assertEqual(1, length(http_get(Config, "/connections"))), 1000, 10),
|
|
[Connection] = http_get(Config, "/connections"),
|
|
ConnectionName = maps:get(name, Connection),
|
|
Path = "/connections/" ++ binary_to_list(uri_string:quote(ConnectionName)) ++ "/sessions",
|
|
[Session] = http_get(Config, Path),
|
|
?assertMatch(
|
|
#{channel_number := 0,
|
|
handle_max := HandleMax,
|
|
next_incoming_id := NextIncomingId,
|
|
incoming_window := IncomingWindow,
|
|
next_outgoing_id := NextOutgoingId,
|
|
remote_incoming_window := RemoteIncomingWindow,
|
|
remote_outgoing_window := RemoteOutgoingWindow,
|
|
outgoing_unsettled_deliveries := 0
|
|
} when is_integer(HandleMax) andalso
|
|
is_integer(NextIncomingId) andalso
|
|
is_integer(IncomingWindow) andalso
|
|
is_integer(NextOutgoingId) andalso
|
|
is_integer(RemoteIncomingWindow) andalso
|
|
is_integer(RemoteOutgoingWindow),
|
|
Session),
|
|
|
|
{ok, IncomingLinks} = maps:find(incoming_links, Session),
|
|
{ok, OutgoingLinks} = maps:find(outgoing_links, Session),
|
|
?assertEqual(2, length(IncomingLinks)),
|
|
?assertEqual(2, length(OutgoingLinks)),
|
|
|
|
?assertMatch([#{handle := 0,
|
|
link_name := <<"my link pair">>,
|
|
target_address := <<"/management">>,
|
|
delivery_count := DeliveryCount1,
|
|
credit := Credit1,
|
|
snd_settle_mode := <<"settled">>,
|
|
max_message_size := IncomingMaxMsgSize,
|
|
unconfirmed_messages := 0},
|
|
#{handle := 2,
|
|
link_name := <<"my sender">>,
|
|
target_address := <<"/exchanges/amq.direct/my%20key">>,
|
|
delivery_count := DeliveryCount2,
|
|
credit := Credit2,
|
|
snd_settle_mode := <<"mixed">>,
|
|
max_message_size := IncomingMaxMsgSize,
|
|
unconfirmed_messages := 0}]
|
|
when is_integer(Credit1) andalso
|
|
is_integer(Credit2) andalso
|
|
is_integer(IncomingMaxMsgSize) andalso
|
|
is_integer(DeliveryCount1) andalso
|
|
is_integer(DeliveryCount2),
|
|
IncomingLinks),
|
|
|
|
[OutLink1, OutLink2] = OutgoingLinks,
|
|
?assertMatch(#{handle := 1,
|
|
link_name := <<"my link pair">>,
|
|
source_address := <<"/management">>,
|
|
queue_name := <<>>,
|
|
delivery_count := DeliveryCount3,
|
|
credit := 0,
|
|
max_message_size := <<"unlimited">>,
|
|
send_settled := true}
|
|
when is_integer(DeliveryCount3),
|
|
OutLink1),
|
|
#{handle := 3,
|
|
link_name := <<"my receiver">>,
|
|
source_address := <<"/queues/my%20stream">>,
|
|
queue_name := <<"my stream">>,
|
|
delivery_count := DeliveryCount4,
|
|
credit := 5000,
|
|
max_message_size := <<"unlimited">>,
|
|
send_settled := true,
|
|
filter := ActualFilter} = OutLink2,
|
|
?assert(is_integer(DeliveryCount4)),
|
|
ExpectedFilter = [#{name => <<"ts filter">>,
|
|
descriptor => <<"rabbitmq:stream-offset-spec">>,
|
|
value => 1751023462000},
|
|
#{name => <<"bloom filter">>,
|
|
descriptor => <<"rabbitmq:stream-filter">>,
|
|
value => [<<"complaint">>, <<"user1">>]},
|
|
#{name => <<"match filter">>,
|
|
descriptor => <<"rabbitmq:stream-match-unfiltered">>,
|
|
value => true},
|
|
#{name => <<"prop filter">>,
|
|
descriptor => ?DESCRIPTOR_CODE_PROPERTIES_FILTER,
|
|
value => [#{key => <<"subject">>, value => <<"complaint">>},
|
|
#{key => <<"user-id">>, value => <<"user1">>}]},
|
|
#{name => <<"app prop filter">>,
|
|
descriptor => ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER,
|
|
value => [#{key => <<"k1">>, value => -4},
|
|
#{key => <<"☀️"/utf8>>, value => <<"🙂"/utf8>>}]}],
|
|
?assertEqual(lists:sort(ExpectedFilter),
|
|
lists:sort(ActualFilter)),
|
|
|
|
{ok, _Session2} = amqp10_client:begin_session_sync(C),
|
|
Sessions = http_get(Config, Path),
|
|
?assertEqual(2, length(Sessions)),
|
|
|
|
ok = amqp10_client:detach_link(Sender),
|
|
ok = amqp10_client:detach_link(Receiver),
|
|
{ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, QName),
|
|
ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair),
|
|
ok = amqp10_client:close_connection(C).
|
|
|
|
%% Test that GET /connections/:name/sessions returns
|
|
%% 400 Bad Request for non-AMQP 1.0 connections.
|
|
amqpl_sessions(Config) ->
|
|
{Conn, _Ch} = open_connection_and_channel(Config),
|
|
LocalPort = local_port(Conn),
|
|
Path = binary_to_list(
|
|
rabbit_mgmt_format:print(
|
|
"/connections/127.0.0.1%3A~w%20-%3E%20127.0.0.1%3A~w/sessions",
|
|
[LocalPort, amqp_port(Config)])),
|
|
ok = await_condition(
|
|
fun() ->
|
|
http_get(Config, Path, 400),
|
|
true
|
|
end).
|
|
|
|
%% Test that AMQP 1.0 connection can be listed if the rabbitmq_management plugin gets enabled
|
|
%% after the connection was established.
|
|
enable_plugin_amqp(Config) ->
|
|
?assertEqual(0, length(http_get(Config, "/connections"))),
|
|
|
|
ok = rabbit_ct_broker_helpers:disable_plugin(Config, 0, rabbitmq_management),
|
|
ok = rabbit_ct_broker_helpers:disable_plugin(Config, 0, rabbitmq_management_agent),
|
|
|
|
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp),
|
|
OpnConf = #{address => ?config(rmq_hostname, Config),
|
|
port => Port,
|
|
container_id => <<"my container">>,
|
|
sasl => anon},
|
|
{ok, Conn} = amqp10_client:open_connection(OpnConf),
|
|
receive {amqp10_event, {connection, Conn, opened}} -> ok
|
|
after 5000 -> ct:fail(opened_timeout)
|
|
end,
|
|
|
|
ok = rabbit_ct_broker_helpers:enable_plugin(Config, 0, rabbitmq_management_agent),
|
|
ok = rabbit_ct_broker_helpers:enable_plugin(Config, 0, rabbitmq_management),
|
|
eventually(?_assertEqual(1, length(http_get(Config, "/connections"))), 1000, 10),
|
|
|
|
ok = amqp10_client:close_connection(Conn),
|
|
receive {amqp10_event, {connection, Conn, {closed, normal}}} -> ok
|
|
after 5000 -> ct:fail({connection_close_timeout, Conn})
|
|
end.
|
|
|
|
flush(Prefix) ->
|
|
receive
|
|
Msg ->
|
|
ct:pal("~ts flushed: ~p~n", [Prefix, Msg]),
|
|
flush(Prefix)
|
|
after 1 ->
|
|
ok
|
|
end.
|
|
|
|
multiple_invalid_connections_test(Config) ->
|
|
Count = 100,
|
|
spawn_invalid(Config, Count),
|
|
Page0 = http_get(Config, "/connections?page=1&page_size=100", ?OK),
|
|
wait_for_answers(Count),
|
|
Page1 = http_get(Config, "/connections?page=1&page_size=100", ?OK),
|
|
?assertEqual(0, maps:get(total_count, Page0)),
|
|
?assertEqual(0, maps:get(total_count, Page1)),
|
|
passed.
|
|
|
|
test_auth(Config, Code, Headers) ->
|
|
{ok, {{_, Code, _}, RespHeaders, _}} = req(Config, get, "/overview", Headers),
|
|
RespHeaders.
|
|
|
|
exchanges_test(Config) ->
|
|
%% Can list exchanges
|
|
http_get(Config, "/exchanges", {group, '2xx'}),
|
|
%% Can pass booleans or strings
|
|
Good = [{type, <<"direct">>}, {durable, <<"true">>}],
|
|
http_put(Config, "/vhosts/myvhost", none, {group, '2xx'}),
|
|
http_get(Config, "/exchanges/myvhost/foo", ?NOT_FOUND),
|
|
http_put(Config, "/exchanges/myvhost/foo", Good, {group, '2xx'}),
|
|
http_put(Config, "/exchanges/myvhost/foo", Good, {group, '2xx'}),
|
|
http_get(Config, "/exchanges/%2F/foo", ?NOT_FOUND),
|
|
assert_item(#{name => <<"foo">>,
|
|
vhost => <<"myvhost">>,
|
|
type => <<"direct">>,
|
|
durable => true,
|
|
auto_delete => false,
|
|
internal => false,
|
|
arguments => #{}},
|
|
http_get(Config, "/exchanges/myvhost/foo")),
|
|
http_put(Config, "/exchanges/badvhost/bar", Good, ?NOT_FOUND),
|
|
http_put(Config, "/exchanges/myvhost/bar", [{type, <<"bad_exchange_type">>}],
|
|
?BAD_REQUEST),
|
|
http_put(Config, "/exchanges/myvhost/bar", [{type, <<"direct">>},
|
|
{durable, <<"troo">>}],
|
|
?BAD_REQUEST),
|
|
http_put(Config, "/exchanges/myvhost/foo", [{type, <<"direct">>}],
|
|
?BAD_REQUEST),
|
|
|
|
http_delete(Config, "/exchanges/myvhost/foo", {group, '2xx'}),
|
|
http_delete(Config, "/exchanges/myvhost/foo", ?NOT_FOUND),
|
|
|
|
http_delete(Config, "/vhosts/myvhost", {group, '2xx'}),
|
|
http_get(Config, "/exchanges/badvhost", ?NOT_FOUND),
|
|
passed.
|
|
|
|
queues_test(Config) ->
|
|
Good = [{durable, true}],
|
|
http_get(Config, "/queues/%2F/foo", ?NOT_FOUND),
|
|
http_put(Config, "/queues/%2F/foo", Good, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/foo", Good, {group, '2xx'}),
|
|
http_get(Config, "/queues/%2F/foo", ?OK),
|
|
|
|
rabbit_ct_broker_helpers:add_vhost(Config, <<"downvhost">>),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, <<"downvhost">>),
|
|
http_put(Config, "/queues/downvhost/foo", Good, {group, '2xx'}),
|
|
http_put(Config, "/queues/downvhost/bar", Good, {group, '2xx'}),
|
|
|
|
rabbit_ct_broker_helpers:force_vhost_failure(Config, <<"downvhost">>),
|
|
%% The vhost is down
|
|
Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
|
|
DownVHost = #{name => <<"downvhost">>, tracing => false, cluster_state => #{Node => <<"stopped">>}},
|
|
assert_item(DownVHost, http_get(Config, "/vhosts/downvhost")),
|
|
|
|
DownQueues = http_get(Config, "/queues/downvhost"),
|
|
DownQueue = http_get(Config, "/queues/downvhost/foo"),
|
|
|
|
assert_list([#{name => <<"bar">>,
|
|
vhost => <<"downvhost">>,
|
|
state => <<"stopped">>,
|
|
durable => true,
|
|
auto_delete => false,
|
|
exclusive => false,
|
|
arguments => #{'x-queue-type' => <<"classic">>}
|
|
},
|
|
#{name => <<"foo">>,
|
|
vhost => <<"downvhost">>,
|
|
state => <<"stopped">>,
|
|
durable => true,
|
|
auto_delete => false,
|
|
exclusive => false,
|
|
arguments => #{'x-queue-type' => <<"classic">>}
|
|
}], DownQueues),
|
|
assert_item(#{name => <<"foo">>,
|
|
vhost => <<"downvhost">>,
|
|
state => <<"stopped">>,
|
|
durable => true,
|
|
auto_delete => false,
|
|
exclusive => false,
|
|
arguments => #{'x-queue-type' => <<"classic">>}
|
|
}, DownQueue),
|
|
|
|
http_put(Config, "/queues/badvhost/bar", Good, ?NOT_FOUND),
|
|
http_put(Config, "/queues/%2F/bar",
|
|
[{durable, <<"troo">>}],
|
|
?BAD_REQUEST),
|
|
http_put(Config, "/queues/%2F/foo",
|
|
[{durable, false}],
|
|
?BAD_REQUEST),
|
|
|
|
http_put(Config, "/queues/%2F/baz", Good, {group, '2xx'}),
|
|
%% Wait until metrics are emitted and stats collected
|
|
?awaitMatch(true, maps:is_key(storage_version,
|
|
http_get(Config, "/queues/%2F/baz")),
|
|
30000),
|
|
Queues = http_get(Config, "/queues/%2F"),
|
|
Queue = http_get(Config, "/queues/%2F/foo"),
|
|
assert_list([#{name => <<"baz">>,
|
|
vhost => <<"/">>,
|
|
durable => true,
|
|
auto_delete => false,
|
|
exclusive => false,
|
|
arguments => #{'x-queue-type' => <<"classic">>},
|
|
storage_version => 2},
|
|
#{name => <<"foo">>,
|
|
vhost => <<"/">>,
|
|
durable => true,
|
|
auto_delete => false,
|
|
exclusive => false,
|
|
arguments => #{'x-queue-type' => <<"classic">>},
|
|
storage_version => 2}], Queues),
|
|
assert_item(#{name => <<"foo">>,
|
|
vhost => <<"/">>,
|
|
durable => true,
|
|
auto_delete => false,
|
|
exclusive => false,
|
|
arguments => #{'x-queue-type' => <<"classic">>},
|
|
storage_version => 2}, Queue),
|
|
|
|
http_delete(Config, "/queues/%2F/foo", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/baz", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/foo", ?NOT_FOUND),
|
|
http_get(Config, "/queues/badvhost", ?NOT_FOUND),
|
|
|
|
http_delete(Config, "/queues/downvhost/foo", {group, '2xx'}),
|
|
http_delete(Config, "/queues/downvhost/bar", {group, '2xx'}),
|
|
passed.
|
|
|
|
quorum_queues_test(Config) ->
|
|
%% Test in a loop that no metrics are left behing after deleting a queue
|
|
quorum_queues_test_loop(Config, 2).
|
|
|
|
quorum_queues_test_loop(_Config, 0) ->
|
|
passed;
|
|
quorum_queues_test_loop(Config, N) ->
|
|
Good = [{durable, true}, {arguments, [{'x-queue-type', 'quorum'}]}],
|
|
http_get(Config, "/queues/%2f/qq", ?NOT_FOUND),
|
|
http_put(Config, "/queues/%2f/qq", Good, {group, '2xx'}),
|
|
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
Publish = fun() ->
|
|
amqp_channel:call(
|
|
Ch, #'basic.publish'{exchange = <<"">>,
|
|
routing_key = <<"qq">>},
|
|
#amqp_msg{payload = <<"message">>})
|
|
end,
|
|
Publish(),
|
|
Publish(),
|
|
rabbit_ct_helpers:await_condition(
|
|
fun() ->
|
|
Num = maps:get(messages, http_get(Config, "/queues/%2f/qq?lengths_age=60&lengths_incr=5&msg_rates_age=60&msg_rates_incr=5&data_rates_age=60&data_rates_incr=5"), undefined),
|
|
2 == Num
|
|
end, ?COLLECT_INTERVAL * 100),
|
|
|
|
http_delete(Config, "/queues/%2f/qq", {group, '2xx'}),
|
|
http_put(Config, "/queues/%2f/qq", Good, {group, '2xx'}),
|
|
|
|
rabbit_ct_helpers:await_condition(
|
|
fun() ->
|
|
0 == maps:get(messages, http_get(Config, "/queues/%2f/qq?lengths_age=60&lengths_incr=5&msg_rates_age=60&msg_rates_incr=5&data_rates_age=60&data_rates_incr=5"), undefined)
|
|
end, ?COLLECT_INTERVAL * 100),
|
|
|
|
http_delete(Config, "/queues/%2f/qq", {group, '2xx'}),
|
|
close_connection(Conn),
|
|
quorum_queues_test_loop(Config, N-1).
|
|
|
|
stream_queues_have_consumers_field(Config) ->
|
|
Good = [{durable, true}, {arguments, [{'x-queue-type', 'stream'}]}],
|
|
http_get(Config, "/queues/%2f/sq", ?NOT_FOUND),
|
|
http_put(Config, "/queues/%2f/sq", Good, {group, '2xx'}),
|
|
|
|
rabbit_ct_helpers:await_condition(
|
|
fun() ->
|
|
Qs = http_get(Config, "/queues/%2F"),
|
|
length(Qs) == 1 andalso maps:is_key(consumers, lists:nth(1, Qs))
|
|
end, ?COLLECT_INTERVAL * 100),
|
|
|
|
Queues = http_get(Config, "/queues/%2F"),
|
|
assert_list([#{name => <<"sq">>,
|
|
arguments => #{'x-queue-type' => <<"stream">>},
|
|
consumers => 0}],
|
|
Queues),
|
|
|
|
http_delete(Config, "/queues/%2f/sq", {group, '2xx'}),
|
|
ok.
|
|
|
|
bindings_test(Config) ->
|
|
XArgs = [{type, <<"direct">>}],
|
|
QArgs = #{},
|
|
http_put(Config, "/exchanges/%2F/myexchange", XArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/myqueue", QArgs, {group, '2xx'}),
|
|
BArgs = [{routing_key, <<"routing">>}, {arguments, []}],
|
|
http_post(Config, "/bindings/%2F/e/myexchange/q/myqueue", BArgs, {group, '2xx'}),
|
|
http_get(Config, "/bindings/%2F/e/myexchange/q/myqueue/routing", ?OK),
|
|
http_get(Config, "/bindings/%2F/e/myexchange/q/myqueue/rooting", ?NOT_FOUND),
|
|
Binding =
|
|
#{source => <<"myexchange">>,
|
|
vhost => <<"/">>,
|
|
destination => <<"myqueue">>,
|
|
destination_type => <<"queue">>,
|
|
routing_key => <<"routing">>,
|
|
arguments => #{},
|
|
properties_key => <<"routing">>},
|
|
DBinding =
|
|
#{source => <<"">>,
|
|
vhost => <<"/">>,
|
|
destination => <<"myqueue">>,
|
|
destination_type => <<"queue">>,
|
|
routing_key => <<"myqueue">>,
|
|
arguments => #{},
|
|
properties_key => <<"myqueue">>},
|
|
Binding = http_get(Config, "/bindings/%2F/e/myexchange/q/myqueue/routing"),
|
|
assert_list([Binding],
|
|
http_get(Config, "/bindings/%2F/e/myexchange/q/myqueue")),
|
|
assert_list([DBinding, Binding],
|
|
http_get(Config, "/queues/%2F/myqueue/bindings")),
|
|
assert_list([Binding],
|
|
http_get(Config, "/exchanges/%2F/myexchange/bindings/source")),
|
|
http_delete(Config, "/bindings/%2F/e/myexchange/q/myqueue/routing", {group, '2xx'}),
|
|
http_delete(Config, "/bindings/%2F/e/myexchange/q/myqueue/routing", ?NOT_FOUND),
|
|
http_delete(Config, "/exchanges/%2F/myexchange", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/myqueue", {group, '2xx'}),
|
|
http_get(Config, "/bindings/badvhost", ?NOT_FOUND),
|
|
http_get(Config, "/bindings/badvhost/myqueue/myexchange/routing", ?NOT_FOUND),
|
|
http_get(Config, "/bindings/%2F/e/myexchange/q/myqueue/routing", ?NOT_FOUND),
|
|
passed.
|
|
|
|
bindings_post_test(Config) ->
|
|
XArgs = [{type, <<"direct">>}],
|
|
QArgs = #{},
|
|
BArgs = [{routing_key, <<"routing">>}, {arguments, [{foo, <<"bar">>}]}],
|
|
http_put(Config, "/exchanges/%2F/myexchange", XArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/myqueue", QArgs, {group, '2xx'}),
|
|
http_post(Config, "/bindings/%2F/e/myexchange/q/badqueue", BArgs, ?NOT_FOUND),
|
|
http_post(Config, "/bindings/%2F/e/badexchange/q/myqueue", BArgs, ?NOT_FOUND),
|
|
|
|
Headers1 = http_post(Config, "/bindings/%2F/e/myexchange/q/myqueue", #{}, {group, '2xx'}),
|
|
Want0 = "myqueue/\~",
|
|
?assertEqual(Want0, pget("location", Headers1)),
|
|
|
|
Headers2 = http_post(Config, "/bindings/%2F/e/myexchange/q/myqueue", BArgs, {group, '2xx'}),
|
|
%% Args hash is calculated from a table, generated from args.
|
|
Hash = table_hash([{<<"foo">>,longstr,<<"bar">>}]),
|
|
PropertiesKey = "routing\~" ++ Hash,
|
|
|
|
Want1 = "myqueue/" ++ PropertiesKey,
|
|
?assertEqual(Want1, pget("location", Headers2)),
|
|
|
|
PropertiesKeyBin = list_to_binary(PropertiesKey),
|
|
Want2 = #{source => <<"myexchange">>,
|
|
vhost => <<"/">>,
|
|
destination => <<"myqueue">>,
|
|
destination_type => <<"queue">>,
|
|
routing_key => <<"routing">>,
|
|
arguments => #{foo => <<"bar">>},
|
|
properties_key => PropertiesKeyBin},
|
|
URI = "/bindings/%2F/e/myexchange/q/myqueue/" ++ PropertiesKey,
|
|
?assertEqual(Want2, http_get(Config, URI, ?OK)),
|
|
|
|
http_get(Config, URI ++ "x", ?NOT_FOUND),
|
|
http_delete(Config, URI, {group, '2xx'}),
|
|
http_delete(Config, "/exchanges/%2F/myexchange", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/myqueue", {group, '2xx'}),
|
|
passed.
|
|
|
|
bindings_null_routing_key_test(Config) ->
|
|
http_delete(Config, "/exchanges/%2F/myexchange", {one_of, [201, 404]}),
|
|
XArgs = [{type, <<"direct">>}],
|
|
QArgs = #{},
|
|
BArgs = [{routing_key, null}, {arguments, #{}}],
|
|
http_put(Config, "/exchanges/%2F/myexchange", XArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/myqueue", QArgs, {group, '2xx'}),
|
|
http_post(Config, "/bindings/%2F/e/myexchange/q/badqueue", BArgs, ?NOT_FOUND),
|
|
http_post(Config, "/bindings/%2F/e/badexchange/q/myqueue", BArgs, ?NOT_FOUND),
|
|
|
|
Headers1 = http_post(Config, "/bindings/%2F/e/myexchange/q/myqueue", #{}, {group, '2xx'}),
|
|
Want0 = "myqueue/\~",
|
|
?assertEqual(Want0, pget("location", Headers1)),
|
|
|
|
Headers2 = http_post(Config, "/bindings/%2F/e/myexchange/q/myqueue", BArgs, {group, '2xx'}),
|
|
%% Args hash is calculated from a table, generated from args.
|
|
Hash = table_hash([]),
|
|
PropertiesKey = "null\~" ++ Hash,
|
|
|
|
?assertEqual("myqueue/null", pget("location", Headers2)),
|
|
Want1 = #{arguments => #{},
|
|
destination => <<"myqueue">>,
|
|
destination_type => <<"queue">>,
|
|
properties_key => <<"null">>,
|
|
routing_key => null,
|
|
source => <<"myexchange">>,
|
|
vhost => <<"/">>},
|
|
URI = "/bindings/%2F/e/myexchange/q/myqueue/" ++ PropertiesKey,
|
|
?assertEqual(Want1, http_get(Config, URI, ?OK)),
|
|
|
|
http_get(Config, URI ++ "x", ?NOT_FOUND),
|
|
http_delete(Config, URI, {group, '2xx'}),
|
|
http_delete(Config, "/exchanges/%2F/myexchange", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/myqueue", {group, '2xx'}),
|
|
passed.
|
|
|
|
bindings_e2e_test(Config) ->
|
|
BArgs = [{routing_key, <<"routing">>}, {arguments, []}],
|
|
http_post(Config, "/bindings/%2F/e/amq.direct/e/badexchange", BArgs, ?NOT_FOUND),
|
|
http_post(Config, "/bindings/%2F/e/badexchange/e/amq.fanout", BArgs, ?NOT_FOUND),
|
|
Headers = http_post(Config, "/bindings/%2F/e/amq.direct/e/amq.fanout", BArgs, {group, '2xx'}),
|
|
"amq.fanout/routing" = pget("location", Headers),
|
|
#{source := <<"amq.direct">>,
|
|
vhost := <<"/">>,
|
|
destination := <<"amq.fanout">>,
|
|
destination_type := <<"exchange">>,
|
|
routing_key := <<"routing">>,
|
|
arguments := #{},
|
|
properties_key := <<"routing">>} =
|
|
http_get(Config, "/bindings/%2F/e/amq.direct/e/amq.fanout/routing", ?OK),
|
|
http_delete(Config, "/bindings/%2F/e/amq.direct/e/amq.fanout/routing", {group, '2xx'}),
|
|
http_post(Config, "/bindings/%2F/e/amq.direct/e/amq.headers", BArgs, {group, '2xx'}),
|
|
Binding =
|
|
#{source => <<"amq.direct">>,
|
|
vhost => <<"/">>,
|
|
destination => <<"amq.headers">>,
|
|
destination_type => <<"exchange">>,
|
|
routing_key => <<"routing">>,
|
|
arguments => #{},
|
|
properties_key => <<"routing">>},
|
|
Binding = http_get(Config, "/bindings/%2F/e/amq.direct/e/amq.headers/routing"),
|
|
assert_list([Binding],
|
|
http_get(Config, "/bindings/%2F/e/amq.direct/e/amq.headers")),
|
|
assert_list([Binding],
|
|
http_get(Config, "/exchanges/%2F/amq.direct/bindings/source")),
|
|
assert_list([Binding],
|
|
http_get(Config, "/exchanges/%2F/amq.headers/bindings/destination")),
|
|
http_delete(Config, "/bindings/%2F/e/amq.direct/e/amq.headers/routing", {group, '2xx'}),
|
|
http_get(Config, "/bindings/%2F/e/amq.direct/e/amq.headers/rooting", ?NOT_FOUND),
|
|
passed.
|
|
|
|
permissions_administrator_test(Config) ->
|
|
http_put(Config, "/users/isadmin", [{password, <<"isadmin">>},
|
|
{tags, <<"administrator">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/notadmin", [{password, <<"notadmin">>},
|
|
{tags, <<"administrator">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/notadmin", [{password, <<"notadmin">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
Test =
|
|
fun(Path) ->
|
|
http_get(Config, Path, "notadmin", "notadmin", ?NOT_AUTHORISED),
|
|
http_get(Config, Path, "isadmin", "isadmin", ?OK),
|
|
http_get(Config, Path, "guest", "guest", ?OK)
|
|
end,
|
|
Test("/vhosts/%2F"),
|
|
Test("/vhosts/%2F/permissions"),
|
|
Test("/users"),
|
|
Test("/users/guest"),
|
|
Test("/users/guest/permissions"),
|
|
Test("/permissions"),
|
|
Test("/permissions/%2F/guest"),
|
|
http_delete(Config, "/users/notadmin", {group, '2xx'}),
|
|
http_delete(Config, "/users/isadmin", {group, '2xx'}),
|
|
passed.
|
|
|
|
permissions_vhost_test(Config) ->
|
|
QArgs = #{},
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/users/myadmin", [{password, <<"myadmin">>},
|
|
{tags, <<"administrator">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/myuser", [{password, <<"myuser">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost1", none, {group, '2xx'}),
|
|
http_put(Config, "/vhosts/myvhost2", none, {group, '2xx'}),
|
|
http_put(Config, "/permissions/myvhost1/myuser", PermArgs, {group, '2xx'}),
|
|
http_put(Config, "/permissions/myvhost1/guest", PermArgs, {group, '2xx'}),
|
|
http_put(Config, "/permissions/myvhost2/guest", PermArgs, {group, '2xx'}),
|
|
assert_list([#{name => <<"/">>},
|
|
#{name => <<"myvhost1">>},
|
|
#{name => <<"myvhost2">>}], http_get(Config, "/vhosts", ?OK)),
|
|
assert_list([#{name => <<"myvhost1">>}],
|
|
http_get(Config, "/vhosts", "myuser", "myuser", ?OK)),
|
|
http_put(Config, "/queues/myvhost1/myqueue", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/myvhost2/myqueue", QArgs, {group, '2xx'}),
|
|
Test1 =
|
|
fun(Path) ->
|
|
Results = http_get(Config, Path, "myuser", "myuser", ?OK),
|
|
[case maps:get(vhost, Result) of
|
|
<<"myvhost2">> ->
|
|
throw({got_result_from_vhost2_in, Path, Result});
|
|
_ ->
|
|
ok
|
|
end || Result <- Results]
|
|
end,
|
|
Test2 =
|
|
fun(Path1, Path2) ->
|
|
http_get(Config, Path1 ++ "/myvhost1/" ++ Path2, "myuser", "myuser",
|
|
?OK),
|
|
http_get(Config, Path1 ++ "/myvhost2/" ++ Path2, "myuser", "myuser",
|
|
?NOT_AUTHORISED)
|
|
end,
|
|
Test3 =
|
|
fun(Path1) ->
|
|
http_get(Config, Path1 ++ "/myvhost1/", "myadmin", "myadmin",
|
|
?OK)
|
|
end,
|
|
Test1("/exchanges"),
|
|
Test2("/exchanges", ""),
|
|
Test2("/exchanges", "amq.direct"),
|
|
Test3("/exchanges"),
|
|
Test1("/queues"),
|
|
Test2("/queues", ""),
|
|
Test3("/queues"),
|
|
Test2("/queues", "myqueue"),
|
|
Test1("/bindings"),
|
|
Test2("/bindings", ""),
|
|
Test3("/bindings"),
|
|
Test2("/queues", "myqueue/bindings"),
|
|
Test2("/exchanges", "amq.default/bindings/source"),
|
|
Test2("/exchanges", "amq.default/bindings/destination"),
|
|
Test2("/bindings", "e/amq.default/q/myqueue"),
|
|
Test2("/bindings", "e/amq.default/q/myqueue/myqueue"),
|
|
http_delete(Config, "/vhosts/myvhost1", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/myvhost2", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
http_delete(Config, "/users/myadmin", {group, '2xx'}),
|
|
passed.
|
|
|
|
permissions_amqp_test(Config) ->
|
|
%% Just test that it works at all, not that it works in all possible cases.
|
|
QArgs = #{},
|
|
PermArgs = [{configure, <<"foo.*">>}, {write, <<"foo.*">>},
|
|
{read, <<"foo.*">>}],
|
|
http_put(Config, "/users/myuser", [{password, <<"myuser">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_put(Config, "/permissions/%2F/myuser", PermArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/bar-queue", QArgs, "myuser", "myuser",
|
|
?NOT_AUTHORISED),
|
|
http_put(Config, "/queues/%2F/bar-queue", QArgs, "nonexistent", "nonexistent",
|
|
?NOT_AUTHORISED),
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
passed.
|
|
|
|
permissions_queue_delete_test(Config) ->
|
|
QArgs = #{},
|
|
PermArgs = [{configure, <<"foo.*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/users/myuser", [{password, <<"myuser">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_put(Config, "/permissions/%2F/myuser", PermArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/bar-queue", QArgs, {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/bar-queue", "myuser", "myuser", ?NOT_AUTHORISED),
|
|
http_delete(Config, "/queues/%2F/bar-queue", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
passed.
|
|
|
|
%% Opens a new connection and a channel on it.
|
|
%% The channel is not managed by rabbit_ct_client_helpers and
|
|
%% should be explicitly closed by the caller.
|
|
open_connection_and_channel(Config) ->
|
|
Conn = rabbit_ct_client_helpers:open_connection(Config, 0),
|
|
{ok, Ch} = amqp_connection:open_channel(Conn),
|
|
{Conn, Ch}.
|
|
|
|
get_conn(Config, Username, Password) ->
|
|
Port = amqp_port(Config),
|
|
{ok, Conn} = amqp_connection:start(#amqp_params_network{
|
|
port = Port,
|
|
username = list_to_binary(Username),
|
|
password = list_to_binary(Password)}),
|
|
LocalPort = local_port(Conn),
|
|
ConnPath = rabbit_misc:format(
|
|
"/connections/127.0.0.1%3A~w%20-%3E%20127.0.0.1%3A~w",
|
|
[LocalPort, Port]),
|
|
ChPath = rabbit_misc:format(
|
|
"/channels/127.0.0.1%3A~w%20-%3E%20127.0.0.1%3A~w%20(1)",
|
|
[LocalPort, Port]),
|
|
ConnChPath = rabbit_misc:format(
|
|
"/connections/127.0.0.1%3A~w%20-%3E%20127.0.0.1%3A~w/channels",
|
|
[LocalPort, Port]),
|
|
{Conn, ConnPath, ChPath, ConnChPath}.
|
|
|
|
permissions_connection_channel_consumer_test(Config) ->
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/users/user", [{password, <<"user">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_put(Config, "/permissions/%2F/user", PermArgs, {group, '2xx'}),
|
|
http_put(Config, "/users/monitor", [{password, <<"monitor">>},
|
|
{tags, <<"monitoring">>}], {group, '2xx'}),
|
|
http_put(Config, "/permissions/%2F/monitor", PermArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/test", #{}, {group, '2xx'}),
|
|
|
|
{Conn1, UserConn, UserCh, UserConnCh} = get_conn(Config, "user", "user"),
|
|
{Conn2, MonConn, MonCh, MonConnCh} = get_conn(Config, "monitor", "monitor"),
|
|
{Conn3, AdmConn, AdmCh, AdmConnCh} = get_conn(Config, "guest", "guest"),
|
|
{ok, Ch1} = amqp_connection:open_channel(Conn1),
|
|
{ok, Ch2} = amqp_connection:open_channel(Conn2),
|
|
{ok, Ch3} = amqp_connection:open_channel(Conn3),
|
|
[amqp_channel:subscribe(
|
|
Ch, #'basic.consume'{queue = <<"test">>}, self()) ||
|
|
Ch <- [Ch1, Ch2, Ch3]],
|
|
AssertLength = fun (Path, User, Len) ->
|
|
Res = http_get(Config, Path, User, User, ?OK),
|
|
?assertEqual(Len, length(Res))
|
|
end,
|
|
await_condition(
|
|
fun () ->
|
|
[begin
|
|
AssertLength(P, "user", 1),
|
|
AssertLength(P, "monitor", 3),
|
|
AssertLength(P, "guest", 3)
|
|
end || P <- ["/connections", "/channels", "/consumers", "/consumers/%2F"]],
|
|
true
|
|
end),
|
|
|
|
AssertRead = fun(Path, UserStatus) ->
|
|
http_get(Config, Path, "user", "user", UserStatus),
|
|
http_get(Config, Path, "monitor", "monitor", ?OK),
|
|
http_get(Config, Path, ?OK)
|
|
end,
|
|
AssertRead(UserConn, ?OK),
|
|
AssertRead(MonConn, ?NOT_AUTHORISED),
|
|
AssertRead(AdmConn, ?NOT_AUTHORISED),
|
|
AssertRead(UserCh, ?OK),
|
|
AssertRead(MonCh, ?NOT_AUTHORISED),
|
|
AssertRead(AdmCh, ?NOT_AUTHORISED),
|
|
AssertRead(UserConnCh, ?OK),
|
|
AssertRead(MonConnCh, ?NOT_AUTHORISED),
|
|
AssertRead(AdmConnCh, ?NOT_AUTHORISED),
|
|
|
|
AssertClose = fun(Path, User, Status) ->
|
|
http_delete(Config, Path, User, User, Status)
|
|
end,
|
|
AssertClose(UserConn, "monitor", ?NOT_AUTHORISED),
|
|
AssertClose(MonConn, "user", ?NOT_AUTHORISED),
|
|
AssertClose(AdmConn, "guest", {group, '2xx'}),
|
|
AssertClose(MonConn, "guest", {group, '2xx'}),
|
|
AssertClose(UserConn, "user", {group, '2xx'}),
|
|
|
|
http_delete(Config, "/users/user", {group, '2xx'}),
|
|
http_delete(Config, "/users/monitor", {group, '2xx'}),
|
|
http_get(Config, "/connections/foo", ?NOT_FOUND),
|
|
http_get(Config, "/channels/foo", ?NOT_FOUND),
|
|
http_delete(Config, "/queues/%2F/test", {group, '2xx'}),
|
|
passed.
|
|
|
|
consumers_cq_test(Config) ->
|
|
consumers_test(Config, [{'x-queue-type', <<"classic">>}]).
|
|
|
|
consumers_qq_test(Config) ->
|
|
consumers_test(Config, [{'x-queue-type', <<"quorum">>}]).
|
|
|
|
consumers_test(Config, Args) ->
|
|
QArgs = [{auto_delete, false}, {durable, true},
|
|
{arguments, Args}],
|
|
http_put(Config, "/queues/%2F/test", QArgs, {group, '2xx'}),
|
|
{Conn, _ConnPath, _ChPath, _ConnChPath} = get_conn(Config, "guest", "guest"),
|
|
{ok, Ch} = amqp_connection:open_channel(Conn),
|
|
amqp_channel:subscribe(
|
|
Ch, #'basic.consume'{queue = <<"test">>,
|
|
no_ack = false,
|
|
consumer_tag = <<"my-ctag">> }, self()),
|
|
await_condition(
|
|
fun () ->
|
|
assert_list([#{exclusive => false,
|
|
ack_required => true,
|
|
active => true,
|
|
activity_status => <<"up">>,
|
|
consumer_tag => <<"my-ctag">>}], http_get(Config, "/consumers")),
|
|
true
|
|
end),
|
|
amqp_connection:close(Conn),
|
|
http_delete(Config, "/queues/%2F/test", {group, '2xx'}),
|
|
passed.
|
|
|
|
single_active_consumer_cq_test(Config) ->
|
|
single_active_consumer(Config,
|
|
"/queues/%2F/single-active-consumer-cq",
|
|
<<"single-active-consumer-cq">>,
|
|
[{'x-queue-type', <<"classic">>}]).
|
|
|
|
single_active_consumer_qq_test(Config) ->
|
|
single_active_consumer(Config,
|
|
"/queues/%2F/single-active-consumer-qq",
|
|
<<"single-active-consumer-qq">>,
|
|
[{'x-queue-type', <<"quorum">>}]).
|
|
|
|
single_active_consumer(Config, Url, QName, Args) ->
|
|
QArgs = [{auto_delete, false}, {durable, true},
|
|
{arguments, [{'x-single-active-consumer', true}] ++ Args}],
|
|
http_put(Config, Url, QArgs, {group, '2xx'}),
|
|
{Conn, _ConnPath, _ChPath, _ConnChPath} = get_conn(Config, "guest", "guest"),
|
|
{ok, Ch} = amqp_connection:open_channel(Conn),
|
|
amqp_channel:subscribe(
|
|
Ch, #'basic.consume'{queue = QName,
|
|
no_ack = true,
|
|
consumer_tag = <<"1">> }, self()),
|
|
{ok, Ch2} = amqp_connection:open_channel(Conn),
|
|
amqp_channel:subscribe(
|
|
Ch2, #'basic.consume'{queue = QName,
|
|
no_ack = true,
|
|
consumer_tag = <<"2">> }, self()),
|
|
await_condition(
|
|
fun () ->
|
|
assert_list([#{exclusive => false,
|
|
ack_required => false,
|
|
active => true,
|
|
activity_status => <<"single_active">>,
|
|
consumer_tag => <<"1">>},
|
|
#{exclusive => false,
|
|
ack_required => false,
|
|
active => false,
|
|
activity_status => <<"waiting">>,
|
|
consumer_tag => <<"2">>}], http_get(Config, "/consumers")),
|
|
true
|
|
end),
|
|
amqp_channel:close(Ch),
|
|
await_condition(
|
|
fun () ->
|
|
assert_list([#{exclusive => false,
|
|
ack_required => false,
|
|
active => true,
|
|
activity_status => <<"single_active">>,
|
|
consumer_tag => <<"2">>}], http_get(Config, "/consumers")),
|
|
true
|
|
end),
|
|
amqp_connection:close(Conn),
|
|
http_delete(Config, Url, {group, '2xx'}),
|
|
passed.
|
|
|
|
defs(Config, Key, URI, CreateMethod, Args) ->
|
|
defs(Config, Key, URI, CreateMethod, Args,
|
|
fun(URI2) -> http_delete(Config, URI2, {group, '2xx'}) end).
|
|
|
|
defs_v(Config, Key, URI, CreateMethod, Args) ->
|
|
Rep1 = fun (S, S2) -> re:replace(S, "<vhost>", S2, [{return, list}]) end,
|
|
ReplaceVHostInArgs = fun(M, V2) -> maps:map(fun(vhost, _) -> V2;
|
|
(_, V1) -> V1 end, M) end,
|
|
|
|
%% Test against default vhost
|
|
defs(Config, Key, Rep1(URI, "%2F"), CreateMethod, ReplaceVHostInArgs(Args, <<"/">>)),
|
|
|
|
%% Test against new vhost
|
|
http_put(Config, "/vhosts/test", none, {group, '2xx'}),
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/permissions/test/guest", PermArgs, {group, '2xx'}),
|
|
DeleteFun0 = fun(URI2) ->
|
|
http_delete(Config, URI2, {group, '2xx'})
|
|
end,
|
|
DeleteFun1 = fun(_) ->
|
|
http_delete(Config, "/vhosts/test", {group, '2xx'})
|
|
end,
|
|
defs(Config, Key, Rep1(URI, "test"),
|
|
CreateMethod, ReplaceVHostInArgs(Args, <<"test">>),
|
|
DeleteFun0, DeleteFun1).
|
|
|
|
create(Config, CreateMethod, URI, Args) ->
|
|
case CreateMethod of
|
|
put -> http_put(Config, URI, Args, {group, '2xx'}),
|
|
URI;
|
|
put_update -> http_put(Config, URI, Args, {group, '2xx'}),
|
|
URI;
|
|
post -> Headers = http_post(Config, URI, Args, {group, '2xx'}),
|
|
rabbit_web_dispatch_util:unrelativise(
|
|
URI, pget("location", Headers))
|
|
end.
|
|
|
|
defs(Config, Key, URI, CreateMethod, Args, DeleteFun) ->
|
|
defs(Config, Key, URI, CreateMethod, Args, DeleteFun, DeleteFun).
|
|
|
|
defs(Config, Key, URI, CreateMethod, Args, DeleteFun0, DeleteFun1) ->
|
|
%% Create the item
|
|
URI2 = create(Config, CreateMethod, URI, Args),
|
|
%% Make sure it ends up in definitions
|
|
Definitions = http_get(Config, "/definitions", ?OK),
|
|
true = lists:any(fun(I) -> test_item(Args, I) end, maps:get(Key, Definitions)),
|
|
|
|
%% Delete it
|
|
DeleteFun0(URI2),
|
|
|
|
%% Post the definitions back, it should get recreated in correct form
|
|
http_post(Config, "/definitions", Definitions, {group, '2xx'}),
|
|
assert_item(Args, http_get(Config, URI2, ?OK)),
|
|
|
|
%% And delete it again
|
|
DeleteFun1(URI2),
|
|
|
|
passed.
|
|
|
|
register_parameters_and_policy_validator(Config) ->
|
|
rpc(Config, rabbit_mgmt_runtime_parameters_util, register, []),
|
|
rpc(Config, rabbit_mgmt_runtime_parameters_util, register_policy_validator, []).
|
|
|
|
unregister_parameters_and_policy_validator(Config) ->
|
|
rpc(Config, rabbit_mgmt_runtime_parameters_util, unregister_policy_validator, []),
|
|
rpc(Config, rabbit_mgmt_runtime_parameters_util, unregister, []).
|
|
|
|
definitions_test(Config) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
|
|
defs_v(Config, queues, "/queues/<vhost>/my-queue", put,
|
|
#{name => <<"my-queue">>,
|
|
durable => true}),
|
|
defs_v(Config, exchanges, "/exchanges/<vhost>/my-exchange", put,
|
|
#{name => <<"my-exchange">>,
|
|
type => <<"direct">>}),
|
|
defs_v(Config, bindings, "/bindings/<vhost>/e/amq.direct/e/amq.fanout", post,
|
|
#{routing_key => <<"routing">>, arguments => #{}}),
|
|
defs_v(Config, policies, "/policies/<vhost>/my-policy", put,
|
|
#{vhost => vhost,
|
|
name => <<"my-policy">>,
|
|
pattern => <<".*">>,
|
|
definition => #{testpos => [1, 2, 3]},
|
|
priority => 1}),
|
|
defs_v(Config, parameters, "/parameters/test/<vhost>/good", put,
|
|
#{vhost => vhost,
|
|
component => <<"test">>,
|
|
name => <<"good">>,
|
|
value => <<"ignore">>}),
|
|
defs(Config, global_parameters, "/global-parameters/good", put,
|
|
#{name => <<"good">>,
|
|
value => #{a => <<"b">>}}),
|
|
defs(Config, users, "/users/myuser", put,
|
|
#{name => <<"myuser">>,
|
|
password_hash => <<"WAbU0ZIcvjTpxM3Q3SbJhEAM2tQ=">>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha256">>,
|
|
tags => [<<"management">>]}),
|
|
defs(Config, vhosts, "/vhosts/myvhost", put,
|
|
#{name => <<"myvhost">>}),
|
|
defs(Config, permissions, "/permissions/%2F/guest", put,
|
|
#{user => <<"guest">>,
|
|
vhost => <<"/">>,
|
|
configure => <<"c">>,
|
|
write => <<"w">>,
|
|
read => <<"r">>}),
|
|
defs(Config, topic_permissions, "/topic-permissions/%2F/guest", put,
|
|
#{user => <<"guest">>,
|
|
vhost => <<"/">>,
|
|
exchange => <<"amq.topic">>,
|
|
write => <<"^a">>,
|
|
read => <<"^b">>}),
|
|
|
|
%% We just messed with guest's permissions
|
|
http_put(Config, "/permissions/%2F/guest",
|
|
#{configure => <<".*">>,
|
|
write => <<".*">>,
|
|
read => <<".*">>}, {group, '2xx'}),
|
|
BrokenConfig =
|
|
#{users => [],
|
|
vhosts => [],
|
|
permissions => [],
|
|
queues => [],
|
|
exchanges => [#{name => <<"not.direct">>,
|
|
vhost => <<"/">>,
|
|
type => <<"definitely not direct">>,
|
|
durable => true,
|
|
auto_delete => false,
|
|
arguments => []}
|
|
],
|
|
bindings => []},
|
|
http_post(Config, "/definitions", BrokenConfig, ?BAD_REQUEST),
|
|
|
|
unregister_parameters_and_policy_validator(Config),
|
|
passed.
|
|
|
|
long_definitions_test(Config) ->
|
|
%% Vhosts take time to start. Generate a bunch of them
|
|
Vhosts = long_definitions_vhosts(long_definitions_test),
|
|
LongDefs =
|
|
#{users => [],
|
|
vhosts => Vhosts,
|
|
permissions => [],
|
|
queues => [],
|
|
exchanges => [],
|
|
bindings => []},
|
|
http_post(Config, "/definitions", LongDefs, {group, '2xx'}),
|
|
passed.
|
|
|
|
long_definitions_multipart_test(Config) ->
|
|
%% Vhosts take time to start. Generate a bunch of them
|
|
Vhosts = long_definitions_vhosts(long_definitions_multipart_test),
|
|
LongDefs =
|
|
#{users => [],
|
|
vhosts => Vhosts,
|
|
permissions => [],
|
|
queues => [],
|
|
exchanges => [],
|
|
bindings => []},
|
|
Data = binary_to_list(format_for_upload(LongDefs)),
|
|
CodeExp = {group, '2xx'},
|
|
Boundary = "------------long_definitions_multipart_test",
|
|
Body = format_multipart_filedata(Boundary, [{file, "file", Data}]),
|
|
ContentType = lists:concat(["multipart/form-data; boundary=", Boundary]),
|
|
MoreHeaders = [{"content-type", ContentType}, {"content-length", integer_to_list(length(Body))}],
|
|
http_upload_raw(Config, post, "/definitions", Body, "guest", "guest", CodeExp, MoreHeaders),
|
|
passed.
|
|
|
|
long_definitions_vhosts(long_definitions_test) ->
|
|
[#{name => <<"long_definitions_test-", (integer_to_binary(N))/binary>>} ||
|
|
N <- lists:seq(1, 120)];
|
|
long_definitions_vhosts(long_definitions_multipart_test) ->
|
|
Bin = list_to_binary(lists:flatten(lists:duplicate(524288, "X"))),
|
|
[#{name => <<"long_definitions_test-", Bin/binary, (integer_to_binary(N))/binary>>} ||
|
|
N <- lists:seq(1, 16)].
|
|
|
|
defs_default_queue_type_vhost(Config, QueueType) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
|
|
%% Create a test vhost
|
|
http_put(Config, "/vhosts/definitions-dqt-vhost-test-vhost", #{default_queue_type => QueueType}, {group, '2xx'}),
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/permissions/definitions-dqt-vhost-test-vhost/guest", PermArgs, {group, '2xx'}),
|
|
|
|
%% Import queue definition without an explicit queue type
|
|
http_post(Config, "/definitions/definitions-dqt-vhost-test-vhost",
|
|
#{queues => [#{name => <<"test-queue">>, durable => true}]},
|
|
{group, '2xx'}),
|
|
|
|
%% And check whether it was indeed created with the default type
|
|
Q = http_get(Config, "/queues/definitions-dqt-vhost-test-vhost/test-queue", ?OK),
|
|
?assertEqual(QueueType, maps:get(type, Q)),
|
|
|
|
%% Remove the test vhost
|
|
http_delete(Config, "/vhosts/definitions-dqt-vhost-test-vhost", {group, '2xx'}),
|
|
ok.
|
|
|
|
definitions_vhost_metadata_test(Config) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
|
|
VHostName = rabbit_data_coercion:to_binary(?FUNCTION_NAME),
|
|
Desc = <<"Created by definitions_vhost_metadata_test">>,
|
|
DQT = <<"quorum">>,
|
|
Tags = [<<"one">>, <<"tag-two">>],
|
|
Metadata = #{
|
|
description => Desc,
|
|
default_queue_type => DQT,
|
|
tags => Tags
|
|
},
|
|
|
|
%% Create a test vhost
|
|
http_put(Config, io_lib:format("/vhosts/~ts", [VHostName]), Metadata, {group, '2xx'}),
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, io_lib:format("/permissions/~ts/guest", [VHostName]), PermArgs, {group, '2xx'}),
|
|
|
|
%% Get the definitions
|
|
Definitions = http_get(Config, "/definitions", ?OK),
|
|
ct:pal("Exported definitions:~n~tp~tn", [Definitions]),
|
|
|
|
%% Check if vhost definition is correct
|
|
VHosts = maps:get(vhosts, Definitions),
|
|
{value, VH} = lists:search(fun(VH) ->
|
|
maps:get(name, VH) =:= VHostName
|
|
end, VHosts),
|
|
?assertEqual(#{
|
|
name => VHostName,
|
|
description => Desc,
|
|
tags => Tags,
|
|
metadata => Metadata
|
|
}, VH),
|
|
|
|
%% Post the definitions back
|
|
http_post(Config, "/definitions", Definitions, {group, '2xx'}),
|
|
|
|
%% Remove the test vhost
|
|
http_delete(Config, io_lib:format("/vhosts/~ts", [VHostName]), {group, '2xx'}),
|
|
ok.
|
|
|
|
definitions_file_metadata_test(Config) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
|
|
VHostName = rabbit_data_coercion:to_binary(?FUNCTION_NAME),
|
|
Desc = <<"Created by definitions_vhost_metadata_test">>,
|
|
DQT = <<"quorum">>,
|
|
Tags = [<<"tag-one">>, <<"tag-two">>],
|
|
Metadata = #{
|
|
description => Desc,
|
|
default_queue_type => DQT,
|
|
tags => Tags
|
|
},
|
|
|
|
http_put(Config, io_lib:format("/vhosts/~ts", [VHostName]), Metadata, {group, '2xx'}),
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, io_lib:format("/permissions/~ts/guest", [VHostName]), PermArgs, {group, '2xx'}),
|
|
|
|
AllDefinitions = http_get(Config, "/definitions", ?OK),
|
|
%% verify definitions file metadata
|
|
?assertEqual(<<"cluster">>, maps:get(rabbitmq_definition_format, AllDefinitions)),
|
|
?assert(is_binary(maps:get(original_cluster_name, AllDefinitions))),
|
|
|
|
%% Post the definitions back
|
|
http_post(Config, "/definitions", AllDefinitions, {group, '2xx'}),
|
|
|
|
VHDefinitions = http_get(Config, io_lib:format("/definitions/~ts", [VHostName]), ?OK),
|
|
%% verify definitions file metadata
|
|
?assertEqual(<<"single_virtual_host">>, maps:get(rabbitmq_definition_format, VHDefinitions)),
|
|
?assertEqual(VHostName, (maps:get(original_vhost_name, VHDefinitions))),
|
|
?assert(is_map_key(default_queue_type, maps:get(metadata, VHDefinitions))),
|
|
|
|
%% Remove the test vhost
|
|
http_delete(Config, io_lib:format("/vhosts/~ts", [VHostName]), {group, '2xx'}),
|
|
ok.
|
|
|
|
definitions_default_queue_type_test(Config) ->
|
|
defs_default_queue_type_vhost(Config, <<"classic">>),
|
|
defs_default_queue_type_vhost(Config, <<"quorum">>).
|
|
|
|
defs_vhost(Config, Key, URI, CreateMethod, Args) ->
|
|
Rep1 = fun (S, S2) -> re:replace(S, "<vhost>", S2, [{return, list}]) end,
|
|
|
|
%% Create a vhost host
|
|
http_put(Config, "/vhosts/defs-vhost-1298379187", none, {group, '2xx'}),
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/permissions/defs-vhost-1298379187/guest", PermArgs, {group, '2xx'}),
|
|
|
|
%% Test against the default vhost
|
|
defs_vhost(Config, Key, URI, Rep1, "%2F", "defs-vhost-1298379187", CreateMethod, Args,
|
|
fun(URI2) -> http_delete(Config, URI2, {group, '2xx'}) end),
|
|
|
|
%% Test against the newly created vhost
|
|
defs_vhost(Config, Key, URI, Rep1, "defs-vhost-1298379187", "%2F", CreateMethod, Args,
|
|
fun(URI2) -> http_delete(Config, URI2, {group, '2xx'}) end),
|
|
|
|
%% Remove the newly created vhost
|
|
http_delete(Config, "/vhosts/defs-vhost-1298379187", {group, '2xx'}).
|
|
|
|
defs_vhost(Config, Key, URI0, Rep1, VHost1, VHost2, CreateMethod, Args,
|
|
DeleteFun) ->
|
|
%% Create the item
|
|
URI2 = create(Config, CreateMethod, Rep1(URI0, VHost1), Args),
|
|
|
|
%% Make sure it ends up in definitions
|
|
Definitions = http_get(Config, "/definitions/" ++ VHost1, ?OK),
|
|
true = lists:any(fun(I) -> test_item(Args, I) end, maps:get(Key, Definitions)),
|
|
|
|
%% `vhost` is implied when importing/exporting for a single
|
|
%% virtual host, let's make sure that it doesn't accidentally
|
|
%% appear in the exported definitions. This can (and did) cause a
|
|
%% confusion about which part of the request to use as the source
|
|
%% for the vhost name.
|
|
case [ I || #{vhost := _} = I <- maps:get(Key, Definitions)] of
|
|
[] -> ok;
|
|
WithVHost -> error({vhost_included_in, Key, WithVHost})
|
|
end,
|
|
|
|
%% Make sure it is not in the other vhost
|
|
Definitions0 = http_get(Config, "/definitions/" ++ VHost2, ?OK),
|
|
false = lists:any(fun(I) -> test_item(Args, I) end, maps:get(Key, Definitions0)),
|
|
|
|
%% Post the definitions back
|
|
http_post(Config, "/definitions/" ++ VHost2, Definitions, {group, '2xx'}),
|
|
|
|
%% Make sure it is now in the other vhost
|
|
Definitions1 = http_get(Config, "/definitions/" ++ VHost2, ?OK),
|
|
true = lists:any(fun(I) -> test_item(Args, I) end, maps:get(Key, Definitions1)),
|
|
|
|
%% Delete it
|
|
DeleteFun(URI2),
|
|
URI3 = create(Config, CreateMethod, Rep1(URI0, VHost2), Args),
|
|
DeleteFun(URI3),
|
|
passed.
|
|
|
|
definitions_vhost_test(Config) ->
|
|
%% Ensures that definitions can be exported/imported from a single virtual
|
|
%% host to another
|
|
|
|
register_parameters_and_policy_validator(Config),
|
|
|
|
defs_vhost(Config, queues, "/queues/<vhost>/definitions-vhost-test-imported-q", put,
|
|
#{name => <<"definitions-vhost-test-imported-q">>,
|
|
durable => true}),
|
|
defs_vhost(Config, exchanges, "/exchanges/<vhost>/definitions-vhost-test-imported-dx", put,
|
|
#{name => <<"definitions-vhost-test-imported-dx">>,
|
|
type => <<"direct">>}),
|
|
defs_vhost(Config, bindings, "/bindings/<vhost>/e/amq.direct/e/amq.fanout", post,
|
|
#{routing_key => <<"routing">>, arguments => #{}}),
|
|
defs_vhost(Config, policies, "/policies/<vhost>/definitions-vhost-test-policy", put,
|
|
#{name => <<"definitions-vhost-test-policy">>,
|
|
pattern => <<".*">>,
|
|
definition => #{testpos => [1, 2, 3]},
|
|
priority => 1}),
|
|
|
|
defs_vhost(Config, parameters, "/parameters/vhost-limits/<vhost>/limits", put,
|
|
#{name => <<"limits">>,
|
|
component => <<"vhost-limits">>,
|
|
value => #{ 'max-connections' => 100 }}),
|
|
Upload =
|
|
#{queues => [],
|
|
exchanges => [],
|
|
policies => [],
|
|
parameters => [],
|
|
bindings => []},
|
|
http_post(Config, "/definitions/othervhost", Upload, ?NOT_FOUND),
|
|
|
|
unregister_parameters_and_policy_validator(Config),
|
|
passed.
|
|
|
|
definitions_password_test(Config) ->
|
|
% Import definitions from 3.5.x
|
|
Config35 = #{rabbit_version => <<"3.5.4">>,
|
|
users => [#{name => <<"myuser">>,
|
|
password_hash => <<"WAbU0ZIcvjTpxM3Q3SbJhEAM2tQ=">>,
|
|
tags => <<"management">>}
|
|
]},
|
|
Expected35 = #{name => <<"myuser">>,
|
|
password_hash => <<"WAbU0ZIcvjTpxM3Q3SbJhEAM2tQ=">>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_md5">>,
|
|
tags => [<<"management">>]},
|
|
http_post(Config, "/definitions", Config35, {group, '2xx'}),
|
|
Definitions35 = http_get(Config, "/definitions", ?OK),
|
|
ct:pal("Definitions35: ~tp", [Definitions35]),
|
|
Users35 = maps:get(users, Definitions35),
|
|
true = lists:any(fun(I) -> test_item(Expected35, I) end, Users35),
|
|
|
|
%% Import definitions from from 3.6.0
|
|
Config36 = #{rabbit_version => <<"3.6.0">>,
|
|
users => [#{name => <<"myuser">>,
|
|
password_hash => <<"WAbU0ZIcvjTpxM3Q3SbJhEAM2tQ=">>,
|
|
tags => <<"management">>}
|
|
]},
|
|
Expected36 = #{name => <<"myuser">>,
|
|
password_hash => <<"WAbU0ZIcvjTpxM3Q3SbJhEAM2tQ=">>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha256">>,
|
|
tags => [<<"management">>]},
|
|
http_post(Config, "/definitions", Config36, {group, '2xx'}),
|
|
|
|
Definitions36 = http_get(Config, "/definitions", ?OK),
|
|
Users36 = maps:get(users, Definitions36),
|
|
true = lists:any(fun(I) -> test_item(Expected36, I) end, Users36),
|
|
|
|
%% No hashing_algorithm provided
|
|
ConfigDefault = #{rabbit_version => <<"3.6.1">>,
|
|
users => [#{name => <<"myuser">>,
|
|
password_hash => <<"WAbU0ZIcvjTpxM3Q3SbJhEAM2tQ=">>,
|
|
tags => <<"management">>}
|
|
]},
|
|
rpc(Config, application, set_env, [rabbit, password_hashing_module, rabbit_password_hashing_sha512]),
|
|
|
|
ExpectedDefault = #{name => <<"myuser">>,
|
|
password_hash => <<"WAbU0ZIcvjTpxM3Q3SbJhEAM2tQ=">>,
|
|
hashing_algorithm => <<"rabbit_password_hashing_sha512">>,
|
|
tags => [<<"management">>]},
|
|
http_post(Config, "/definitions", ConfigDefault, {group, '2xx'}),
|
|
|
|
DefinitionsDefault = http_get(Config, "/definitions", ?OK),
|
|
UsersDefault = maps:get(users, DefinitionsDefault),
|
|
|
|
true = lists:any(fun(I) -> test_item(ExpectedDefault, I) end, UsersDefault),
|
|
passed.
|
|
|
|
definitions_remove_things_test(Config) ->
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
amqp_channel:call(Ch, #'queue.declare'{ queue = <<"my-exclusive">>,
|
|
exclusive = true }),
|
|
http_get(Config, "/queues/%2F/my-exclusive", ?OK),
|
|
Definitions = http_get(Config, "/definitions", ?OK),
|
|
[] = maps:get(queues, Definitions),
|
|
[] = maps:get(exchanges, Definitions),
|
|
[] = maps:get(bindings, Definitions),
|
|
amqp_channel:close(Ch),
|
|
close_connection(Conn),
|
|
passed.
|
|
|
|
definitions_server_named_queue_test(Config) ->
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
%% declares a durable server-named queue for the sake of exporting the definition
|
|
#'queue.declare_ok'{queue = QName} =
|
|
amqp_channel:call(Ch, #'queue.declare'{queue = <<"">>, durable = true}),
|
|
Path = "/queues/%2F/" ++ rabbit_http_util:quote_plus(QName),
|
|
http_get(Config, Path, ?OK),
|
|
Definitions = http_get(Config, "/definitions", ?OK),
|
|
close_channel(Ch),
|
|
close_connection(Conn),
|
|
http_delete(Config, Path, {group, '2xx'}),
|
|
http_get(Config, Path, ?NOT_FOUND),
|
|
http_post(Config, "/definitions", Definitions, {group, '2xx'}),
|
|
%% amq.* entities are not imported
|
|
http_get(Config, Path, ?NOT_FOUND),
|
|
passed.
|
|
|
|
definitions_with_charset_test(Config) ->
|
|
Path = "/definitions",
|
|
Body0 = http_get(Config, Path, ?OK),
|
|
Headers = [auth_header("guest", "guest")],
|
|
Url = uri_base_from(Config, 0) ++ Path,
|
|
Body1 = format_for_upload(Body0),
|
|
Request = {Url, Headers, "application/json; charset=utf-8", Body1},
|
|
{ok, {{_, ?NO_CONTENT, _}, _, []}} = httpc:request(post, Request, ?HTTPC_OPTS, []),
|
|
passed.
|
|
|
|
arguments_test(Config) ->
|
|
XArgs = [{type, <<"headers">>},
|
|
{arguments, [{'alternate-exchange', <<"amq.direct">>}]}],
|
|
QArgs = [{arguments, [{'x-expires', 1800000}]}],
|
|
BArgs = [{routing_key, <<"">>},
|
|
{arguments, [{'x-match', <<"all">>},
|
|
{foo, <<"bar">>}]}],
|
|
http_delete(Config, "/exchanges/%2F/arguments-test-x", {one_of, [201, 404]}),
|
|
http_put(Config, "/exchanges/%2F/arguments-test-x", XArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/arguments-test", QArgs, {group, '2xx'}),
|
|
http_post(Config, "/bindings/%2F/e/arguments-test-x/q/arguments-test", BArgs, {group, '2xx'}),
|
|
|
|
#{'alternate-exchange' := <<"amq.direct">>} =
|
|
maps:get(arguments, http_get(Config, "/exchanges/%2F/arguments-test-x", ?OK)),
|
|
#{'x-expires' := 1800000} =
|
|
maps:get(arguments, http_get(Config, "/queues/%2F/arguments-test", ?OK)),
|
|
|
|
ArgsTable = [{<<"foo">>,longstr,<<"bar">>}, {<<"x-match">>, longstr, <<"all">>}],
|
|
Hash = table_hash(ArgsTable),
|
|
PropertiesKey = [$~] ++ Hash,
|
|
|
|
assert_item(
|
|
#{'x-match' => <<"all">>, foo => <<"bar">>},
|
|
maps:get(arguments,
|
|
http_get(Config, "/bindings/%2F/e/arguments-test-x/q/arguments-test/" ++
|
|
PropertiesKey, ?OK))
|
|
),
|
|
http_delete(Config, "/exchanges/%2F/arguments-test-x", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/arguments-test", {group, '2xx'}),
|
|
passed.
|
|
|
|
table_hash(Table) ->
|
|
binary_to_list(rabbit_mgmt_format:args_hash(Table)).
|
|
|
|
arguments_table_test(Config) ->
|
|
Args = #{'upstreams' => [<<"amqp://localhost/%2F/upstream1">>,
|
|
<<"amqp://localhost/%2F/upstream2">>]},
|
|
XArgs = #{type => <<"headers">>,
|
|
arguments => Args},
|
|
http_delete(Config, "/exchanges/%2F/arguments-table-test-x", {one_of, [201, 404]}),
|
|
http_put(Config, "/exchanges/%2F/arguments-table-test-x", XArgs, {group, '2xx'}),
|
|
Args = maps:get(arguments, http_get(Config, "/exchanges/%2F/arguments-table-test-x", ?OK)),
|
|
http_delete(Config, "/exchanges/%2F/arguments-table-test-x", {group, '2xx'}),
|
|
passed.
|
|
|
|
queue_purge_test(Config) ->
|
|
QArgs = #{},
|
|
http_put(Config, "/queues/%2F/myqueue", QArgs, {group, '2xx'}),
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
Publish = fun() ->
|
|
amqp_channel:call(
|
|
Ch, #'basic.publish'{exchange = <<"">>,
|
|
routing_key = <<"myqueue">>},
|
|
#amqp_msg{payload = <<"message">>})
|
|
end,
|
|
Publish(),
|
|
Publish(),
|
|
amqp_channel:call(
|
|
Ch, #'queue.declare'{queue = <<"exclusive">>, exclusive = true}),
|
|
{#'basic.get_ok'{}, _} =
|
|
amqp_channel:call(Ch, #'basic.get'{queue = <<"myqueue">>}),
|
|
http_delete(Config, "/queues/%2F/myqueue/contents", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/badqueue/contents", ?NOT_FOUND),
|
|
http_delete(Config, "/queues/%2F/exclusive/contents", ?BAD_REQUEST),
|
|
http_delete(Config, "/queues/%2F/exclusive", ?BAD_REQUEST),
|
|
#'basic.get_empty'{} =
|
|
amqp_channel:call(Ch, #'basic.get'{queue = <<"myqueue">>}),
|
|
close_channel(Ch),
|
|
close_connection(Conn),
|
|
http_delete(Config, "/queues/%2F/myqueue", {group, '2xx'}),
|
|
passed.
|
|
|
|
queue_actions_test(Config) ->
|
|
http_put(Config, "/queues/%2F/q", #{}, {group, '2xx'}),
|
|
http_post(Config, "/queues/%2F/q/actions", [{action, change_colour}], ?BAD_REQUEST),
|
|
http_delete(Config, "/queues/%2F/q", {group, '2xx'}),
|
|
passed.
|
|
|
|
exclusive_consumer_test(Config) ->
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
#'queue.declare_ok'{ queue = QName } =
|
|
amqp_channel:call(Ch, #'queue.declare'{exclusive = true}),
|
|
amqp_channel:subscribe(Ch, #'basic.consume'{queue = QName,
|
|
exclusive = true}, self()),
|
|
await_condition(
|
|
fun () ->
|
|
http_get(Config, "/queues/%2F/"), %% Just check we don't blow up
|
|
true
|
|
end),
|
|
close_channel(Ch),
|
|
close_connection(Conn),
|
|
passed.
|
|
|
|
|
|
exclusive_queue_test(Config) ->
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
#'queue.declare_ok'{ queue = QName } =
|
|
amqp_channel:call(Ch, #'queue.declare'{exclusive = true}),
|
|
Path = "/queues/%2F/" ++ rabbit_http_util:quote_plus(QName),
|
|
await_condition(
|
|
fun () ->
|
|
Queue = http_get(Config, Path),
|
|
assert_item(#{name => QName,
|
|
vhost => <<"/">>,
|
|
durable => false,
|
|
auto_delete => false,
|
|
exclusive => true,
|
|
arguments => #{'x-queue-type' => <<"classic">>}
|
|
}, Queue),
|
|
true
|
|
end),
|
|
amqp_channel:close(Ch),
|
|
close_connection(Conn),
|
|
passed.
|
|
|
|
connections_channels_pagination_test(Config) ->
|
|
%% this test uses "unmanaged" (by Common Test helpers) connections to avoid
|
|
%% connection caching
|
|
Conn = open_unmanaged_connection(Config),
|
|
{ok, Ch} = amqp_connection:open_channel(Conn),
|
|
Conn1 = open_unmanaged_connection(Config),
|
|
{ok, Ch1} = amqp_connection:open_channel(Conn1),
|
|
Conn2 = open_unmanaged_connection(Config),
|
|
{ok, Ch2} = amqp_connection:open_channel(Conn2),
|
|
|
|
await_condition(
|
|
fun () ->
|
|
PageOfTwo = http_get(Config, "/connections?page=1&page_size=2", ?OK),
|
|
?assertEqual(3, maps:get(total_count, PageOfTwo)),
|
|
?assertEqual(3, maps:get(filtered_count, PageOfTwo)),
|
|
?assertEqual(2, maps:get(item_count, PageOfTwo)),
|
|
?assertEqual(1, maps:get(page, PageOfTwo)),
|
|
?assertEqual(2, maps:get(page_size, PageOfTwo)),
|
|
?assertEqual(2, maps:get(page_count, PageOfTwo)),
|
|
|
|
|
|
TwoOfTwo = http_get(Config, "/channels?page=2&page_size=2", ?OK),
|
|
?assertEqual(3, maps:get(total_count, TwoOfTwo)),
|
|
?assertEqual(3, maps:get(filtered_count, TwoOfTwo)),
|
|
?assertEqual(1, maps:get(item_count, TwoOfTwo)),
|
|
?assertEqual(2, maps:get(page, TwoOfTwo)),
|
|
?assertEqual(2, maps:get(page_size, TwoOfTwo)),
|
|
?assertEqual(2, maps:get(page_count, TwoOfTwo)),
|
|
true
|
|
end),
|
|
|
|
amqp_channel:close(Ch),
|
|
amqp_connection:close(Conn),
|
|
amqp_channel:close(Ch1),
|
|
amqp_connection:close(Conn1),
|
|
amqp_channel:close(Ch2),
|
|
amqp_connection:close(Conn2),
|
|
|
|
passed.
|
|
|
|
exchanges_pagination_test(Config) ->
|
|
QArgs = #{},
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/vhosts/vh1", none, {group, '2xx'}),
|
|
http_put(Config, "/permissions/vh1/guest", PermArgs, {group, '2xx'}),
|
|
http_get(Config, "/exchanges/vh1?page=1&page_size=2", ?OK),
|
|
http_put(Config, "/exchanges/%2F/test0", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/exchanges/vh1/test1", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/exchanges/%2F/test2_reg", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/exchanges/vh1/reg_test3", QArgs, {group, '2xx'}),
|
|
|
|
Total = length(rpc(Config, rabbit_exchange, list_names, [])),
|
|
await_condition(
|
|
fun () ->
|
|
PageOfTwo = http_get(Config, "/exchanges?page=1&page_size=2", ?OK),
|
|
?assertEqual(Total, maps:get(total_count, PageOfTwo)),
|
|
?assertEqual(Total, maps:get(filtered_count, PageOfTwo)),
|
|
?assertEqual(2, maps:get(item_count, PageOfTwo)),
|
|
?assertEqual(1, maps:get(page, PageOfTwo)),
|
|
?assertEqual(2, maps:get(page_size, PageOfTwo)),
|
|
?assertEqual(round(Total / 2), maps:get(page_count, PageOfTwo)),
|
|
assert_list([#{name => <<"">>, vhost => <<"/">>},
|
|
#{name => <<"amq.direct">>, vhost => <<"/">>}
|
|
], maps:get(items, PageOfTwo)),
|
|
|
|
ByName = http_get(Config, "/exchanges?page=1&page_size=2&name=reg", ?OK),
|
|
?assertEqual(Total, maps:get(total_count, ByName)),
|
|
?assertEqual(2, maps:get(filtered_count, ByName)),
|
|
?assertEqual(2, maps:get(item_count, ByName)),
|
|
?assertEqual(1, maps:get(page, ByName)),
|
|
?assertEqual(2, maps:get(page_size, ByName)),
|
|
?assertEqual(1, maps:get(page_count, ByName)),
|
|
assert_list([#{name => <<"test2_reg">>, vhost => <<"/">>},
|
|
#{name => <<"reg_test3">>, vhost => <<"vh1">>}
|
|
], maps:get(items, ByName)),
|
|
|
|
|
|
RegExByName = http_get(Config,
|
|
"/exchanges?page=1&page_size=2&name=%5E(?=%5Ereg)&use_regex=true",
|
|
?OK),
|
|
?assertEqual(Total, maps:get(total_count, RegExByName)),
|
|
?assertEqual(1, maps:get(filtered_count, RegExByName)),
|
|
?assertEqual(1, maps:get(item_count, RegExByName)),
|
|
?assertEqual(1, maps:get(page, RegExByName)),
|
|
?assertEqual(2, maps:get(page_size, RegExByName)),
|
|
?assertEqual(1, maps:get(page_count, RegExByName)),
|
|
assert_list([#{name => <<"reg_test3">>, vhost => <<"vh1">>}
|
|
], maps:get(items, RegExByName)),
|
|
true
|
|
end),
|
|
|
|
|
|
http_get(Config, "/exchanges?page=1000", ?BAD_REQUEST),
|
|
http_get(Config, "/exchanges?page=-1", ?BAD_REQUEST),
|
|
http_get(Config, "/exchanges?page=not_an_integer_value", ?BAD_REQUEST),
|
|
http_get(Config, "/exchanges?page=1&page_size=not_an_integer_value", ?BAD_REQUEST),
|
|
http_get(Config, "/exchanges?page=1&page_size=501", ?BAD_REQUEST), %% max 500 allowed
|
|
http_get(Config, "/exchanges?page=-1&page_size=-2", ?BAD_REQUEST),
|
|
http_delete(Config, "/exchanges/%2F/test0", {group, '2xx'}),
|
|
http_delete(Config, "/exchanges/vh1/test1", {group, '2xx'}),
|
|
http_delete(Config, "/exchanges/%2F/test2_reg", {group, '2xx'}),
|
|
http_delete(Config, "/exchanges/vh1/reg_test3", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/vh1", {group, '2xx'}),
|
|
passed.
|
|
|
|
exchanges_pagination_permissions_test(Config) ->
|
|
http_put(Config, "/users/admin", [{password, <<"admin">>},
|
|
{tags, <<"administrator">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/non-admin", [{password, <<"non-admin">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
Perms = [{configure, <<".*">>},
|
|
{write, <<".*">>},
|
|
{read, <<".*">>}],
|
|
http_put(Config, "/vhosts/vh1", none, {group, '2xx'}),
|
|
http_put(Config, "/permissions/vh1/non-admin", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/%2F/admin", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/vh1/admin", Perms, {group, '2xx'}),
|
|
QArgs = #{},
|
|
http_put(Config, "/exchanges/%2F/test0", QArgs, "admin", "admin", {group, '2xx'}),
|
|
http_put(Config, "/exchanges/vh1/test1", QArgs, "non-admin", "non-admin", {group, '2xx'}),
|
|
|
|
await_condition(
|
|
fun () ->
|
|
FirstPage = http_get(Config, "/exchanges?page=1&name=test1", "non-admin", "non-admin", ?OK),
|
|
|
|
?assertEqual(8, maps:get(total_count, FirstPage)),
|
|
?assertEqual(1, maps:get(item_count, FirstPage)),
|
|
?assertEqual(1, maps:get(page, FirstPage)),
|
|
?assertEqual(100, maps:get(page_size, FirstPage)),
|
|
?assertEqual(1, maps:get(page_count, FirstPage)),
|
|
assert_list([#{name => <<"test1">>, vhost => <<"vh1">>}
|
|
], maps:get(items, FirstPage)),
|
|
true
|
|
end),
|
|
http_delete(Config, "/exchanges/%2F/test0", {group, '2xx'}),
|
|
http_delete(Config, "/exchanges/vh1/test1", {group, '2xx'}),
|
|
http_delete(Config, "/users/admin", {group, '2xx'}),
|
|
http_delete(Config, "/users/non-admin", {group, '2xx'}),
|
|
passed.
|
|
|
|
|
|
|
|
queue_pagination_test(Config) ->
|
|
QArgs = #{},
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/vhosts/vh.tests.queue_pagination_test", none, {group, '2xx'}),
|
|
http_put(Config, "/permissions/vh.tests.queue_pagination_test/guest", PermArgs, {group, '2xx'}),
|
|
|
|
http_get(Config, "/queues/vh.tests.queue_pagination_test?page=1&page_size=2", ?OK),
|
|
|
|
http_put(Config, "/queues/%2F/test0", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/vh.tests.queue_pagination_test/test1", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/test2_reg", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/vh.tests.queue_pagination_test/reg_test3", QArgs, {group, '2xx'}),
|
|
|
|
?AWAIT(
|
|
begin
|
|
Total = length(rpc(Config, rabbit_amqqueue, list_names, [])),
|
|
|
|
PageOfTwo = http_get(Config, "/queues?page=1&page_size=2", ?OK),
|
|
?assertEqual(Total, maps:get(total_count, PageOfTwo)),
|
|
?assertEqual(Total, maps:get(filtered_count, PageOfTwo)),
|
|
?assertEqual(2, maps:get(item_count, PageOfTwo)),
|
|
?assertEqual(1, maps:get(page, PageOfTwo)),
|
|
?assertEqual(2, maps:get(page_size, PageOfTwo)),
|
|
?assertEqual(2, maps:get(page_count, PageOfTwo)),
|
|
assert_list([#{name => <<"test0">>, vhost => <<"/">>, storage_version => 2},
|
|
#{name => <<"test2_reg">>, vhost => <<"/">>, storage_version => 2}
|
|
], maps:get(items, PageOfTwo)),
|
|
|
|
SortedByName = http_get(Config, "/queues?sort=name&page=1&page_size=2", ?OK),
|
|
?assertEqual(Total, maps:get(total_count, SortedByName)),
|
|
?assertEqual(Total, maps:get(filtered_count, SortedByName)),
|
|
?assertEqual(2, maps:get(item_count, SortedByName)),
|
|
?assertEqual(1, maps:get(page, SortedByName)),
|
|
?assertEqual(2, maps:get(page_size, SortedByName)),
|
|
?assertEqual(2, maps:get(page_count, SortedByName)),
|
|
assert_list([#{name => <<"reg_test3">>, vhost => <<"vh.tests.queue_pagination_test">>},
|
|
#{name => <<"test0">>, vhost => <<"/">>}
|
|
], maps:get(items, SortedByName)),
|
|
|
|
|
|
FirstPage = http_get(Config, "/queues?page=1", ?OK),
|
|
?assertEqual(Total, maps:get(total_count, FirstPage)),
|
|
?assertEqual(Total, maps:get(filtered_count, FirstPage)),
|
|
?assertEqual(4, maps:get(item_count, FirstPage)),
|
|
?assertEqual(1, maps:get(page, FirstPage)),
|
|
?assertEqual(100, maps:get(page_size, FirstPage)),
|
|
?assertEqual(1, maps:get(page_count, FirstPage)),
|
|
assert_list([#{name => <<"test0">>, vhost => <<"/">>},
|
|
#{name => <<"test1">>, vhost => <<"vh.tests.queue_pagination_test">>},
|
|
#{name => <<"test2_reg">>, vhost => <<"/">>},
|
|
#{name => <<"reg_test3">>, vhost =><<"vh.tests.queue_pagination_test">>}
|
|
], maps:get(items, FirstPage)),
|
|
%% The reduced API version just has the most useful fields.
|
|
%% garbage_collection is not one of them
|
|
IsEnabled = rabbit_ct_broker_helpers:is_feature_flag_enabled(
|
|
Config, detailed_queues_endpoint),
|
|
case IsEnabled of
|
|
true ->
|
|
[?assertNot(maps:is_key(garbage_collection, Item)) ||
|
|
Item <- maps:get(items, FirstPage)];
|
|
false ->
|
|
[?assert(maps:is_key(garbage_collection, Item)) ||
|
|
Item <- maps:get(items, FirstPage)]
|
|
end,
|
|
ReverseSortedByName = http_get(Config,
|
|
"/queues?page=2&page_size=2&sort=name&sort_reverse=true",
|
|
?OK),
|
|
?assertEqual(Total, maps:get(total_count, ReverseSortedByName)),
|
|
?assertEqual(Total, maps:get(filtered_count, ReverseSortedByName)),
|
|
?assertEqual(2, maps:get(item_count, ReverseSortedByName)),
|
|
?assertEqual(2, maps:get(page, ReverseSortedByName)),
|
|
?assertEqual(2, maps:get(page_size, ReverseSortedByName)),
|
|
?assertEqual(2, maps:get(page_count, ReverseSortedByName)),
|
|
assert_list([#{name => <<"test0">>, vhost => <<"/">>},
|
|
#{name => <<"reg_test3">>, vhost => <<"vh.tests.queue_pagination_test">>}
|
|
], maps:get(items, ReverseSortedByName)),
|
|
|
|
|
|
ByName = http_get(Config, "/queues?page=1&page_size=2&name=reg", ?OK),
|
|
?assertEqual(Total, maps:get(total_count, ByName)),
|
|
?assertEqual(2, maps:get(filtered_count, ByName)),
|
|
?assertEqual(2, maps:get(item_count, ByName)),
|
|
?assertEqual(1, maps:get(page, ByName)),
|
|
?assertEqual(2, maps:get(page_size, ByName)),
|
|
?assertEqual(1, maps:get(page_count, ByName)),
|
|
assert_list([#{name => <<"test2_reg">>, vhost => <<"/">>},
|
|
#{name => <<"reg_test3">>, vhost => <<"vh.tests.queue_pagination_test">>}
|
|
], maps:get(items, ByName)),
|
|
|
|
RegExByName = http_get(Config,
|
|
"/queues?page=1&page_size=2&name=%5E(?=%5Ereg)&use_regex=true",
|
|
?OK),
|
|
?assertEqual(Total, maps:get(total_count, RegExByName)),
|
|
?assertEqual(1, maps:get(filtered_count, RegExByName)),
|
|
?assertEqual(1, maps:get(item_count, RegExByName)),
|
|
?assertEqual(1, maps:get(page, RegExByName)),
|
|
?assertEqual(2, maps:get(page_size, RegExByName)),
|
|
?assertEqual(1, maps:get(page_count, RegExByName)),
|
|
assert_list([#{name => <<"reg_test3">>, vhost => <<"vh.tests.queue_pagination_test">>}
|
|
], maps:get(items, RegExByName)),
|
|
true
|
|
end
|
|
),
|
|
|
|
|
|
http_get(Config, "/queues?page=1000", ?BAD_REQUEST),
|
|
http_get(Config, "/queues?page=-1", ?BAD_REQUEST),
|
|
http_get(Config, "/queues?page=not_an_integer_value", ?BAD_REQUEST),
|
|
http_get(Config, "/queues?page=1&page_size=not_an_integer_value", ?BAD_REQUEST),
|
|
http_get(Config, "/queues?page=1&page_size=501", ?BAD_REQUEST), %% max 500 allowed
|
|
http_get(Config, "/queues?page=-1&page_size=-2", ?BAD_REQUEST),
|
|
http_delete(Config, "/queues/%2F/test0", {group, '2xx'}),
|
|
http_delete(Config, "/queues/vh.tests.queue_pagination_test/test1", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/test2_reg", {group, '2xx'}),
|
|
http_delete(Config, "/queues/vh.tests.queue_pagination_test/reg_test3", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/vh.tests.queue_pagination_test", {group, '2xx'}),
|
|
passed.
|
|
|
|
queue_pagination_columns_test(Config) ->
|
|
QArgs = #{},
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/vhosts/vh.tests.queue_pagination_columns_test", none, [?CREATED, ?NO_CONTENT]),
|
|
http_put(Config, "/permissions/vh.tests.queue_pagination_columns_test/guest", PermArgs, [?CREATED, ?NO_CONTENT]),
|
|
|
|
http_get(Config, "/queues/vh.tests.queue_pagination_columns_test?columns=name&page=1&page_size=2", ?OK),
|
|
http_put(Config, "/queues/%2F/queue_a", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/vh.tests.queue_pagination_columns_test/queue_b", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/queue_c", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/vh.tests.queue_pagination_columns_test/queue_d", QArgs, {group, '2xx'}),
|
|
PageOfTwo = http_get(Config, "/queues?columns=name&page=1&page_size=2", ?OK),
|
|
?assertEqual(4, maps:get(total_count, PageOfTwo)),
|
|
?assertEqual(4, maps:get(filtered_count, PageOfTwo)),
|
|
?assertEqual(2, maps:get(item_count, PageOfTwo)),
|
|
?assertEqual(1, maps:get(page, PageOfTwo)),
|
|
?assertEqual(2, maps:get(page_size, PageOfTwo)),
|
|
?assertEqual(2, maps:get(page_count, PageOfTwo)),
|
|
assert_list([#{name => <<"queue_a">>},
|
|
#{name => <<"queue_c">>}
|
|
], maps:get(items, PageOfTwo)),
|
|
|
|
ColumnNameVhost = http_get(Config, "/queues/vh.tests.queue_pagination_columns_test?columns=name&page=1&page_size=2", ?OK),
|
|
?assertEqual(2, maps:get(total_count, ColumnNameVhost)),
|
|
?assertEqual(2, maps:get(filtered_count, ColumnNameVhost)),
|
|
?assertEqual(2, maps:get(item_count, ColumnNameVhost)),
|
|
?assertEqual(1, maps:get(page, ColumnNameVhost)),
|
|
?assertEqual(2, maps:get(page_size, ColumnNameVhost)),
|
|
?assertEqual(1, maps:get(page_count, ColumnNameVhost)),
|
|
assert_list([#{name => <<"queue_b">>},
|
|
#{name => <<"queue_d">>}
|
|
], maps:get(items, ColumnNameVhost)),
|
|
|
|
ColumnsNameVhost = http_get(Config, "/queues?columns=name,vhost&page=2&page_size=2", ?OK),
|
|
?assertEqual(4, maps:get(total_count, ColumnsNameVhost)),
|
|
?assertEqual(4, maps:get(filtered_count, ColumnsNameVhost)),
|
|
?assertEqual(2, maps:get(item_count, ColumnsNameVhost)),
|
|
?assertEqual(2, maps:get(page, ColumnsNameVhost)),
|
|
?assertEqual(2, maps:get(page_size, ColumnsNameVhost)),
|
|
?assertEqual(2, maps:get(page_count, ColumnsNameVhost)),
|
|
assert_list([
|
|
#{name => <<"queue_b">>,
|
|
vhost => <<"vh.tests.queue_pagination_columns_test">>},
|
|
#{name => <<"queue_d">>,
|
|
vhost => <<"vh.tests.queue_pagination_columns_test">>}
|
|
], maps:get(items, ColumnsNameVhost)),
|
|
|
|
?awaitMatch(
|
|
true,
|
|
begin
|
|
ColumnsGarbageCollection = http_get(Config, "/queues?columns=name,garbage_collection&page=2&page_size=2", ?OK),
|
|
%% The reduced API version just has the most useful fields,
|
|
%% but we can still query any info item using `columns`
|
|
lists:all(fun(Item) ->
|
|
maps:is_key(garbage_collection, Item)
|
|
end,
|
|
maps:get(items, ColumnsGarbageCollection))
|
|
end, 30000),
|
|
|
|
http_delete(Config, "/queues/%2F/queue_a", {group, '2xx'}),
|
|
http_delete(Config, "/queues/vh.tests.queue_pagination_columns_test/queue_b", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/queue_c", {group, '2xx'}),
|
|
http_delete(Config, "/queues/vh.tests.queue_pagination_columns_test/queue_d", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/vh.tests.queue_pagination_columns_test", {group, '2xx'}),
|
|
passed.
|
|
|
|
queues_detailed_test(Config) ->
|
|
QArgs = #{},
|
|
http_put(Config, "/queues/%2F/queue_a", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/queue_c", QArgs, {group, '2xx'}),
|
|
|
|
?awaitMatch(
|
|
true,
|
|
begin
|
|
Detailed = http_get(Config, "/queues/detailed", ?OK),
|
|
lists:all(fun(Item) ->
|
|
maps:is_key(garbage_collection, Item)
|
|
end, Detailed)
|
|
end, 30000),
|
|
|
|
Detailed = http_get(Config, "/queues/detailed", ?OK),
|
|
?assertNot(lists:any(fun(Item) ->
|
|
maps:is_key(backing_queue_status, Item)
|
|
end, Detailed)),
|
|
%% It's null
|
|
?assert(lists:any(fun(Item) ->
|
|
maps:is_key(single_active_consumer_tag, Item)
|
|
end, Detailed)),
|
|
|
|
Reduced = http_get(Config, "/queues", ?OK),
|
|
?assertNot(lists:any(fun(Item) ->
|
|
maps:is_key(garbage_collection, Item)
|
|
end, Reduced)),
|
|
?assertNot(lists:any(fun(Item) ->
|
|
maps:is_key(backing_queue_status, Item)
|
|
end, Reduced)),
|
|
?assertNot(lists:any(fun(Item) ->
|
|
maps:is_key(single_active_consumer_tag, Item)
|
|
end, Reduced)),
|
|
|
|
http_delete(Config, "/queues/%2F/queue_a", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/queue_c", {group, '2xx'}),
|
|
passed.
|
|
|
|
queues_pagination_permissions_test(Config) ->
|
|
http_put(Config, "/users/non-admin", [{password, <<"non-admin">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/admin", [{password, <<"admin">>},
|
|
{tags, <<"administrator">>}], {group, '2xx'}),
|
|
Perms = [{configure, <<".*">>},
|
|
{write, <<".*">>},
|
|
{read, <<".*">>}],
|
|
http_put(Config, "/vhosts/vh1", none, {group, '2xx'}),
|
|
http_put(Config, "/permissions/vh1/non-admin", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/%2F/admin", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/vh1/admin", Perms, {group, '2xx'}),
|
|
QArgs = #{},
|
|
http_put(Config, "/queues/%2F/test0", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/vh1/test1", QArgs, "non-admin","non-admin", {group, '2xx'}),
|
|
FirstPage = http_get(Config, "/queues?page=1", "non-admin", "non-admin", ?OK),
|
|
?assertEqual(1, maps:get(total_count, FirstPage)),
|
|
?assertEqual(1, maps:get(item_count, FirstPage)),
|
|
?assertEqual(1, maps:get(page, FirstPage)),
|
|
?assertEqual(100, maps:get(page_size, FirstPage)),
|
|
?assertEqual(1, maps:get(page_count, FirstPage)),
|
|
assert_list([#{name => <<"test1">>, vhost => <<"vh1">>}
|
|
], maps:get(items, FirstPage)),
|
|
|
|
FirstPageAdm = http_get(Config, "/queues?page=1", "admin", "admin", ?OK),
|
|
?assertEqual(2, maps:get(total_count, FirstPageAdm)),
|
|
?assertEqual(2, maps:get(item_count, FirstPageAdm)),
|
|
?assertEqual(1, maps:get(page, FirstPageAdm)),
|
|
?assertEqual(100, maps:get(page_size, FirstPageAdm)),
|
|
?assertEqual(1, maps:get(page_count, FirstPageAdm)),
|
|
assert_list([#{name => <<"test1">>, vhost => <<"vh1">>},
|
|
#{name => <<"test0">>, vhost => <<"/">>}
|
|
], maps:get(items, FirstPageAdm)),
|
|
|
|
http_delete(Config, "/queues/%2F/test0", {group, '2xx'}),
|
|
http_delete(Config, "/queues/vh1/test1","admin","admin", {group, '2xx'}),
|
|
http_delete(Config, "/users/admin", {group, '2xx'}),
|
|
http_delete(Config, "/users/non-admin", {group, '2xx'}),
|
|
passed.
|
|
|
|
samples_range_test(Config) ->
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
|
|
%% Channels
|
|
?AWAIT(
|
|
begin
|
|
[ConnInfo | _] = http_get(Config, "/channels?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/channels?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
ConnDetails = maps:get(connection_details, ConnInfo),
|
|
ConnName0 = maps:get(name, ConnDetails),
|
|
ConnName = uri_string:recompose(#{path => binary_to_list(ConnName0)}),
|
|
ChanName = ConnName ++ uri_string:recompose(#{path => " (1)"}),
|
|
|
|
http_get(Config, "/channels/" ++ ChanName ++ "?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/channels/" ++ ChanName ++ "?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
http_get(Config, "/vhosts/%2F/channels?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/vhosts/%2F/channels?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
%% Connections.
|
|
|
|
http_get(Config, "/connections?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/connections?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
http_get(Config, "/connections/" ++ ConnName ++ "?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/connections/" ++ ConnName ++ "?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
http_get(Config, "/connections/" ++ ConnName ++ "/channels?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/connections/" ++ ConnName ++ "/channels?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
http_get(Config, "/vhosts/%2F/connections?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/vhosts/%2F/connections?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
true
|
|
end),
|
|
|
|
amqp_channel:close(Ch),
|
|
amqp_connection:close(Conn),
|
|
|
|
%% Exchanges
|
|
|
|
http_get(Config, "/exchanges?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/exchanges?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
http_get(Config, "/exchanges/%2F/amq.direct?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/exchanges/%2F/amq.direct?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
%% Nodes
|
|
http_get(Config, "/nodes?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/nodes?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
%% Overview
|
|
http_get(Config, "/overview?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/overview?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
|
|
%% Queues
|
|
http_put(Config, "/queues/%2F/test-001", #{}, {group, '2xx'}),
|
|
|
|
?AWAIT(
|
|
begin
|
|
http_get(Config, "/queues/%2F?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/queues/%2F?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
http_get(Config, "/queues/%2F/test-001?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/queues/%2F/test-001?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
true
|
|
end),
|
|
|
|
http_delete(Config, "/queues/%2F/test-001", {group, '2xx'}),
|
|
|
|
%% Vhosts
|
|
http_put(Config, "/vhosts/vh1", none, {group, '2xx'}),
|
|
|
|
?AWAIT(
|
|
begin
|
|
http_get(Config, "/vhosts?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/vhosts?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
http_get(Config, "/vhosts/vh1?lengths_age=60&lengths_incr=1", ?OK),
|
|
http_get(Config, "/vhosts/vh1?lengths_age=6000&lengths_incr=1", ?BAD_REQUEST),
|
|
true
|
|
end),
|
|
|
|
http_delete(Config, "/vhosts/vh1", {group, '2xx'}),
|
|
|
|
passed.
|
|
|
|
sorting_test(Config) ->
|
|
QArgs = #{},
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/vhosts/vh19", none, {group, '2xx'}),
|
|
http_put(Config, "/permissions/vh19/guest", PermArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/test0", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/vh19/test1", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/test2", QArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/vh19/test3", QArgs, {group, '2xx'}),
|
|
?AWAIT(
|
|
begin
|
|
assert_list([#{name => <<"test0">>},
|
|
#{name => <<"test2">>},
|
|
#{name => <<"test1">>},
|
|
#{name => <<"test3">>}], http_get(Config, "/queues", ?OK)),
|
|
assert_list([#{name => <<"test0">>},
|
|
#{name => <<"test1">>},
|
|
#{name => <<"test2">>},
|
|
#{name => <<"test3">>}], http_get(Config, "/queues?sort=name", ?OK)),
|
|
assert_list([#{name => <<"test0">>},
|
|
#{name => <<"test2">>},
|
|
#{name => <<"test1">>},
|
|
#{name => <<"test3">>}], http_get(Config, "/queues?sort=vhost", ?OK)),
|
|
assert_list([#{name => <<"test3">>},
|
|
#{name => <<"test1">>},
|
|
#{name => <<"test2">>},
|
|
#{name => <<"test0">>}], http_get(Config, "/queues?sort_reverse=true", ?OK)),
|
|
assert_list([#{name => <<"test3">>},
|
|
#{name => <<"test2">>},
|
|
#{name => <<"test1">>},
|
|
#{name => <<"test0">>}], http_get(Config, "/queues?sort=name&sort_reverse=true", ?OK)),
|
|
assert_list([#{name => <<"test3">>},
|
|
#{name => <<"test1">>},
|
|
#{name => <<"test2">>},
|
|
#{name => <<"test0">>}], http_get(Config, "/queues?sort=vhost&sort_reverse=true", ?OK)),
|
|
true
|
|
end),
|
|
%% Rather poor but at least test it doesn't blow up with dots
|
|
http_get(Config, "/queues?sort=owner_pid_details.name", ?OK),
|
|
http_delete(Config, "/queues/%2F/test0", {group, '2xx'}),
|
|
http_delete(Config, "/queues/vh19/test1", {group, '2xx'}),
|
|
http_delete(Config, "/queues/%2F/test2", {group, '2xx'}),
|
|
http_delete(Config, "/queues/vh19/test3", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/vh19", {group, '2xx'}),
|
|
passed.
|
|
|
|
format_output_test(Config) ->
|
|
QArgs = #{},
|
|
PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}],
|
|
http_put(Config, "/vhosts/vh129", none, {group, '2xx'}),
|
|
http_put(Config, "/permissions/vh129/guest", PermArgs, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/test0", QArgs, {group, '2xx'}),
|
|
|
|
?AWAIT(
|
|
begin
|
|
assert_list([#{name => <<"test0">>,
|
|
consumer_capacity => 0,
|
|
consumer_utilisation => 0,
|
|
exclusive_consumer_tag => null}], http_get(Config, "/queues", ?OK)),
|
|
true
|
|
end),
|
|
http_delete(Config, "/queues/%2F/test0", {group, '2xx'}),
|
|
http_delete(Config, "/vhosts/vh129", {group, '2xx'}),
|
|
passed.
|
|
|
|
columns_test(Config) ->
|
|
Path = "/queues/%2F/columns.test",
|
|
TTL = 30000,
|
|
http_delete(Config, Path, [{group, '2xx'}, 404]),
|
|
http_put(Config, Path, [{arguments, [{<<"x-message-ttl">>, TTL}]}],
|
|
{group, '2xx'}),
|
|
Item = #{arguments => #{'x-message-ttl' => TTL, 'x-queue-type' => <<"classic">>}, name => <<"columns.test">>},
|
|
|
|
?AWAIT(
|
|
begin
|
|
[Item] = http_get(Config, "/queues?columns=arguments.x-message-ttl,name", ?OK),
|
|
Item = http_get(Config, "/queues/%2F/columns.test?columns=arguments.x-message-ttl,name", ?OK),
|
|
true
|
|
end),
|
|
http_delete(Config, Path, {group, '2xx'}),
|
|
passed.
|
|
|
|
get_test(Config) ->
|
|
%% Real world example...
|
|
Headers = [{<<"x-forwarding">>, array,
|
|
[{table,
|
|
[{<<"uri">>, longstr,
|
|
<<"amqp://localhost/%2F/upstream">>}]}]}],
|
|
http_put(Config, "/queues/%2F/myqueue", #{}, {group, '2xx'}),
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
#'confirm.select_ok'{} = amqp_channel:call(Ch, #'confirm.select'{}),
|
|
Publish = fun (Payload) ->
|
|
amqp_channel:cast(
|
|
Ch, #'basic.publish'{exchange = <<>>,
|
|
routing_key = <<"myqueue">>},
|
|
#amqp_msg{props = #'P_basic'{headers = Headers},
|
|
payload = Payload}),
|
|
amqp_channel:wait_for_confirms_or_die(Ch, 5)
|
|
end,
|
|
Publish(<<"1aaa">>),
|
|
Publish(<<"2aaa">>),
|
|
Publish(<<"3aaa">>),
|
|
[Msg] = http_post(Config, "/queues/%2F/myqueue/get", [{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto},
|
|
{truncate, 1}], ?OK),
|
|
false = maps:get(redelivered, Msg),
|
|
<<>> = maps:get(exchange, Msg),
|
|
<<"myqueue">> = maps:get(routing_key, Msg),
|
|
<<"1">> = maps:get(payload, Msg),
|
|
#{'x-forwarding' :=
|
|
[#{uri := <<"amqp://localhost/%2F/upstream">>}]} =
|
|
maps:get(headers, maps:get(properties, Msg)),
|
|
|
|
[M2, M3] = http_post(Config, "/queues/%2F/myqueue/get", [{ackmode, ack_requeue_true},
|
|
{count, 5},
|
|
{encoding, auto}], ?OK),
|
|
<<"2aaa">> = maps:get(payload, M2),
|
|
<<"3aaa">> = maps:get(payload, M3),
|
|
2 = length(http_post(Config, "/queues/%2F/myqueue/get", [{ackmode, ack_requeue_false},
|
|
{count, 5},
|
|
{encoding, auto}], ?OK)),
|
|
Publish(<<"4aaa">>),
|
|
Publish(<<"5aaa">>),
|
|
[M4, M5] = http_post(Config, "/queues/%2F/myqueue/get",
|
|
[{ackmode, reject_requeue_true},
|
|
{count, 5},
|
|
{encoding, auto}], ?OK),
|
|
|
|
<<"4aaa">> = maps:get(payload, M4),
|
|
<<"5aaa">> = maps:get(payload, M5),
|
|
2 = length(http_post(Config, "/queues/%2F/myqueue/get",
|
|
[{ackmode, ack_requeue_false},
|
|
{count, 5},
|
|
{encoding, auto}], ?OK)),
|
|
|
|
[] = http_post(Config, "/queues/%2F/myqueue/get", [{ackmode, ack_requeue_false},
|
|
{count, 5},
|
|
{encoding, auto}], ?OK),
|
|
http_delete(Config, "/queues/%2F/myqueue", {group, '2xx'}),
|
|
amqp_channel:close(Ch),
|
|
close_connection(Conn),
|
|
|
|
passed.
|
|
|
|
get_encoding_test(Config) ->
|
|
Utf8Text = <<"Loïc was here!"/utf8>>,
|
|
Utf8Payload = base64:encode(Utf8Text),
|
|
BinPayload = base64:encode(<<0:64, 16#ff, 16#fd, 0:64>>),
|
|
Utf8Msg = msg(<<"get_encoding_test">>, #{}, Utf8Payload, <<"base64">>),
|
|
BinMsg = msg(<<"get_encoding_test">>, #{}, BinPayload, <<"base64">>),
|
|
http_put(Config, "/queues/%2F/get_encoding_test", #{}, {group, '2xx'}),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Utf8Msg, ?OK),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", BinMsg, ?OK),
|
|
|
|
[RecvUtf8Msg1, RecvBinMsg1] = http_post(Config, "/queues/%2F/get_encoding_test/get",
|
|
[{ackmode, ack_requeue_false},
|
|
{count, 2},
|
|
{encoding, auto}], ?OK),
|
|
%% Utf-8 payload must be returned as a utf-8 string when auto encoding is used.
|
|
?assertEqual(<<"string">>, maps:get(payload_encoding, RecvUtf8Msg1)),
|
|
?assertEqual(Utf8Text, maps:get(payload, RecvUtf8Msg1)),
|
|
%% Binary payload must be base64-encoded when auto is used.
|
|
?assertEqual(<<"base64">>, maps:get(payload_encoding, RecvBinMsg1)),
|
|
?assertEqual(BinPayload, maps:get(payload, RecvBinMsg1)),
|
|
%% Good. Now try forcing the base64 encoding.
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Utf8Msg, ?OK),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", BinMsg, ?OK),
|
|
[RecvUtf8Msg2, RecvBinMsg2] = http_post(Config, "/queues/%2F/get_encoding_test/get",
|
|
[{ackmode, ack_requeue_false},
|
|
{count, 2},
|
|
{encoding, base64}], ?OK),
|
|
%% All payloads must be base64-encoded when base64 encoding is used.
|
|
?assertEqual(<<"base64">>, maps:get(payload_encoding, RecvUtf8Msg2)),
|
|
?assertEqual(Utf8Payload, maps:get(payload, RecvUtf8Msg2)),
|
|
?assertEqual(<<"base64">>, maps:get(payload_encoding, RecvBinMsg2)),
|
|
?assertEqual(BinPayload, maps:get(payload, RecvBinMsg2)),
|
|
http_delete(Config, "/queues/%2F/get_encoding_test", {group, '2xx'}),
|
|
passed.
|
|
|
|
get_fail_test(Config) ->
|
|
http_put(Config, "/users/myuser", [{password, <<"password">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/myqueue", #{}, {group, '2xx'}),
|
|
http_post(Config, "/queues/%2F/myqueue/get",
|
|
[{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto}], "myuser", "password", ?NOT_AUTHORISED),
|
|
http_delete(Config, "/queues/%2F/myqueue", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
passed.
|
|
|
|
|
|
-define(LARGE_BODY_BYTES, 5000000).
|
|
|
|
publish_test(Config) ->
|
|
Headers = #{'x-forwarding' => [#{uri => <<"amqp://localhost/%2F/upstream">>}]},
|
|
Msg = msg(<<"publish_test">>, Headers, <<"Hello world">>),
|
|
http_put(Config, "/queues/%2F/publish_test", #{}, {group, '2xx'}),
|
|
?assertEqual(#{routed => true},
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Msg, ?OK)),
|
|
[Msg2] = http_post(Config, "/queues/%2F/publish_test/get", [{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto}], ?OK),
|
|
assert_item(Msg, Msg2),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Msg2, ?OK),
|
|
[Msg3] = http_post(Config, "/queues/%2F/publish_test/get", [{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto}], ?OK),
|
|
assert_item(Msg, Msg3),
|
|
http_delete(Config, "/queues/%2F/publish_test", {group, '2xx'}),
|
|
passed.
|
|
|
|
publish_large_message_test(Config) ->
|
|
Headers = #{'x-forwarding' => [#{uri => <<"amqp://localhost/%2F/upstream">>}]},
|
|
Body = binary:copy(<<"a">>, ?LARGE_BODY_BYTES),
|
|
Msg = msg(<<"publish_accept_json_test">>, Headers, Body),
|
|
http_put(Config, "/queues/%2F/publish_accept_json_test", #{}, {group, '2xx'}),
|
|
?assertEqual(#{routed => true},
|
|
http_post_accept_json(Config, "/exchanges/%2F/amq.default/publish",
|
|
Msg, ?OK)),
|
|
|
|
[Msg2] = http_post_accept_json(Config, "/queues/%2F/publish_accept_json_test/get",
|
|
[{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto}], ?OK),
|
|
assert_item(Msg, Msg2),
|
|
http_post_accept_json(Config, "/exchanges/%2F/amq.default/publish", Msg2, ?OK),
|
|
[Msg3] = http_post_accept_json(Config, "/queues/%2F/publish_accept_json_test/get",
|
|
[{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto}], ?OK),
|
|
assert_item(Msg, Msg3),
|
|
http_delete(Config, "/queues/%2F/publish_accept_json_test", {group, '2xx'}),
|
|
passed.
|
|
|
|
-define(EXCESSIVELY_LARGE_BODY_BYTES, 35000000).
|
|
|
|
publish_large_message_exceeding_http_request_body_size_test(Config) ->
|
|
Headers = #{'x-forwarding' => [#{uri => <<"amqp://localhost/%2F/upstream">>}]},
|
|
Body = binary:copy(<<"a">>, ?EXCESSIVELY_LARGE_BODY_BYTES),
|
|
Msg = msg(<<"large_message_exceeding_http_request_body_size_test">>, Headers, Body),
|
|
http_put(Config, "/queues/%2F/large_message_exceeding_http_request_body_size_test", #{}, {group, '2xx'}),
|
|
%% exceeds the default HTTP API request body size limit
|
|
http_post_accept_json(Config, "/exchanges/%2F/amq.default/publish",
|
|
Msg, ?BAD_REQUEST),
|
|
http_delete(Config, "/queues/%2F/large_message_exceeding_http_request_body_size_test", {group, '2xx'}),
|
|
passed.
|
|
|
|
publish_accept_json_test(Config) ->
|
|
Headers = #{'x-forwarding' => [#{uri => <<"amqp://localhost/%2F/upstream">>}]},
|
|
Msg = msg(<<"publish_accept_json_test">>, Headers, <<"Hello world">>),
|
|
http_put(Config, "/queues/%2F/publish_accept_json_test", #{}, {group, '2xx'}),
|
|
?assertEqual(#{routed => true},
|
|
http_post_accept_json(Config, "/exchanges/%2F/amq.default/publish",
|
|
Msg, ?OK)),
|
|
|
|
[Msg2] = http_post_accept_json(Config, "/queues/%2F/publish_accept_json_test/get",
|
|
[{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto}], ?OK),
|
|
assert_item(Msg, Msg2),
|
|
http_post_accept_json(Config, "/exchanges/%2F/amq.default/publish", Msg2, ?OK),
|
|
[Msg3] = http_post_accept_json(Config, "/queues/%2F/publish_accept_json_test/get",
|
|
[{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto}], ?OK),
|
|
assert_item(Msg, Msg3),
|
|
http_delete(Config, "/queues/%2F/publish_accept_json_test", {group, '2xx'}),
|
|
passed.
|
|
|
|
publish_fail_test(Config) ->
|
|
Msg = msg(<<"publish_fail_test">>, [], <<"Hello world">>),
|
|
http_put(Config, "/queues/%2F/publish_fail_test", #{}, {group, '2xx'}),
|
|
http_put(Config, "/users/myuser", [{password, <<"password">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Msg, "myuser", "password",
|
|
?NOT_AUTHORISED),
|
|
Msg2 = [{exchange, <<"">>},
|
|
{routing_key, <<"publish_fail_test">>},
|
|
{properties, [{user_id, <<"foo">>}]},
|
|
{payload, <<"Hello world">>},
|
|
{payload_encoding, <<"string">>}],
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Msg2, ?BAD_REQUEST),
|
|
Msg3 = [{exchange, <<"">>},
|
|
{routing_key, <<"publish_fail_test">>},
|
|
{properties, []},
|
|
{payload, [<<"not a string">>]},
|
|
{payload_encoding, <<"string">>}],
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Msg3, ?BAD_REQUEST),
|
|
MsgTemplate = [{exchange, <<"">>},
|
|
{routing_key, <<"publish_fail_test">>},
|
|
{payload, <<"Hello world">>},
|
|
{payload_encoding, <<"string">>}],
|
|
[http_post(Config, "/exchanges/%2F/amq.default/publish",
|
|
[{properties, [BadProp]} | MsgTemplate], ?BAD_REQUEST)
|
|
|| BadProp <- [{priority, <<"really high">>},
|
|
{timestamp, <<"recently">>},
|
|
{expiration, 1234}]],
|
|
http_delete(Config, "/queues/%2F/publish_fail_test", {group, '2xx'}),
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
passed.
|
|
|
|
publish_base64_test(Config) ->
|
|
%% "abcd"
|
|
%% @todo Note that we used to accept [] instead of {struct, []} when we shouldn't have.
|
|
%% This is a breaking change and probably needs to be documented.
|
|
Msg = msg(<<"publish_base64_test">>, #{}, <<"YWJjZA==">>, <<"base64">>),
|
|
BadMsg1 = msg(<<"publish_base64_test">>, #{}, <<"flibble">>, <<"base64">>),
|
|
BadMsg2 = msg(<<"publish_base64_test">>, #{}, <<"YWJjZA==">>, <<"base99">>),
|
|
http_put(Config, "/queues/%2F/publish_base64_test", #{}, {group, '2xx'}),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Msg, ?OK),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", BadMsg1, ?BAD_REQUEST),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", BadMsg2, ?BAD_REQUEST),
|
|
|
|
[Msg2] = http_post(Config, "/queues/%2F/publish_base64_test/get", [{ackmode, ack_requeue_false},
|
|
{count, 1},
|
|
{encoding, auto}], ?OK),
|
|
?assertEqual(<<"abcd">>, maps:get(payload, Msg2)),
|
|
http_delete(Config, "/queues/%2F/publish_base64_test", {group, '2xx'}),
|
|
passed.
|
|
|
|
publish_unrouted_test(Config) ->
|
|
Msg = msg(<<"hmmm">>, #{}, <<"Hello world">>),
|
|
?assertEqual(#{routed => false},
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish", Msg, ?OK)).
|
|
|
|
if_empty_unused_test(Config) ->
|
|
http_put(Config, "/exchanges/%2F/test", #{}, {group, '2xx'}),
|
|
http_put(Config, "/queues/%2F/test", #{}, {group, '2xx'}),
|
|
http_post(Config, "/bindings/%2F/e/test/q/test", #{}, {group, '2xx'}),
|
|
http_post(Config, "/exchanges/%2F/amq.default/publish",
|
|
msg(<<"test">>, #{}, <<"Hello world">>), ?OK),
|
|
http_delete(Config, "/queues/%2F/test?if-empty=true", ?BAD_REQUEST),
|
|
http_delete(Config, "/exchanges/%2F/test?if-unused=true", ?BAD_REQUEST),
|
|
http_delete(Config, "/queues/%2F/test/contents", {group, '2xx'}),
|
|
|
|
{Conn, _ConnPath, _ChPath, _ConnChPath} = get_conn(Config, "guest", "guest"),
|
|
{ok, Ch} = amqp_connection:open_channel(Conn),
|
|
amqp_channel:subscribe(Ch, #'basic.consume'{queue = <<"test">> }, self()),
|
|
http_delete(Config, "/queues/%2F/test?if-unused=true", ?BAD_REQUEST),
|
|
amqp_connection:close(Conn),
|
|
|
|
http_delete(Config, "/queues/%2F/test?if-empty=true", {group, '2xx'}),
|
|
http_delete(Config, "/exchanges/%2F/test?if-unused=true", {group, '2xx'}),
|
|
passed.
|
|
|
|
parameters_test(Config) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
|
|
http_put(Config, "/parameters/test/%2F/good", [{value, <<"ignore">>}], {group, '2xx'}),
|
|
http_put(Config, "/parameters/test/%2F/maybe", [{value, <<"good">>}], {group, '2xx'}),
|
|
http_put(Config, "/parameters/test/%2F/maybe", [{value, <<"bad">>}], ?BAD_REQUEST),
|
|
http_put(Config, "/parameters/test/%2F/bad", [{value, <<"good">>}], ?BAD_REQUEST),
|
|
http_put(Config, "/parameters/test/um/good", [{value, <<"ignore">>}], ?NOT_FOUND),
|
|
|
|
Good = #{vhost => <<"/">>,
|
|
component => <<"test">>,
|
|
name => <<"good">>,
|
|
value => <<"ignore">>},
|
|
Maybe = #{vhost => <<"/">>,
|
|
component => <<"test">>,
|
|
name => <<"maybe">>,
|
|
value => <<"good">>},
|
|
List = [Good, Maybe],
|
|
|
|
assert_list(List, http_get(Config, "/parameters")),
|
|
assert_list(List, http_get(Config, "/parameters/test")),
|
|
assert_list(List, http_get(Config, "/parameters/test/%2F")),
|
|
assert_list([], http_get(Config, "/parameters/oops")),
|
|
http_get(Config, "/parameters/test/oops", ?NOT_FOUND),
|
|
|
|
assert_item(Good, http_get(Config, "/parameters/test/%2F/good", ?OK)),
|
|
assert_item(Maybe, http_get(Config, "/parameters/test/%2F/maybe", ?OK)),
|
|
|
|
http_delete(Config, "/parameters/test/%2F/good", {group, '2xx'}),
|
|
http_delete(Config, "/parameters/test/%2F/maybe", {group, '2xx'}),
|
|
http_delete(Config, "/parameters/test/%2F/bad", ?NOT_FOUND),
|
|
|
|
0 = length(http_get(Config, "/parameters")),
|
|
0 = length(http_get(Config, "/parameters/test")),
|
|
0 = length(http_get(Config, "/parameters/test/%2F")),
|
|
unregister_parameters_and_policy_validator(Config),
|
|
passed.
|
|
|
|
global_parameters_test(Config) ->
|
|
InitialParameters = http_get(Config, "/global-parameters"),
|
|
http_put(Config, "/global-parameters/good", [{value, [{a, <<"b">>}]}], {group, '2xx'}),
|
|
http_put(Config, "/global-parameters/maybe", [{value,[{c, <<"d">>}]}], {group, '2xx'}),
|
|
|
|
Good = #{name => <<"good">>,
|
|
value => #{a => <<"b">>}},
|
|
Maybe = #{name => <<"maybe">>,
|
|
value => #{c => <<"d">>}},
|
|
List = InitialParameters ++ [Good, Maybe],
|
|
|
|
assert_list(List, http_get(Config, "/global-parameters")),
|
|
http_get(Config, "/global-parameters/oops", ?NOT_FOUND),
|
|
|
|
assert_item(Good, http_get(Config, "/global-parameters/good", ?OK)),
|
|
assert_item(Maybe, http_get(Config, "/global-parameters/maybe", ?OK)),
|
|
|
|
http_delete(Config, "/global-parameters/good", {group, '2xx'}),
|
|
http_delete(Config, "/global-parameters/maybe", {group, '2xx'}),
|
|
http_delete(Config, "/global-parameters/bad", ?NOT_FOUND),
|
|
|
|
InitialCount = length(InitialParameters),
|
|
InitialCount = length(http_get(Config, "/global-parameters")),
|
|
passed.
|
|
|
|
operator_policy_test(Config) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
PolicyPos = #{vhost => <<"/">>,
|
|
name => <<"policy_pos">>,
|
|
pattern => <<".*">>,
|
|
definition => #{testpos => [1,2,3]},
|
|
priority => 10},
|
|
PolicyEven = #{vhost => <<"/">>,
|
|
name => <<"policy_even">>,
|
|
pattern => <<".*">>,
|
|
definition => #{testeven => [1,2,3,4]},
|
|
priority => 10},
|
|
http_put(Config,
|
|
"/operator-policies/%2F/policy_pos",
|
|
PolicyPos,
|
|
{group, '2xx'}),
|
|
http_put(Config,
|
|
"/operator-policies/%2F/policy_even",
|
|
PolicyEven,
|
|
{group, '2xx'}),
|
|
assert_item(PolicyPos, http_get(Config, "/operator-policies/%2F/policy_pos", ?OK)),
|
|
assert_item(PolicyEven, http_get(Config, "/operator-policies/%2F/policy_even", ?OK)),
|
|
List = [PolicyPos, PolicyEven],
|
|
assert_list(List, http_get(Config, "/operator-policies", ?OK)),
|
|
assert_list(List, http_get(Config, "/operator-policies/%2F", ?OK)),
|
|
|
|
http_delete(Config, "/operator-policies/%2F/policy_pos", {group, '2xx'}),
|
|
http_delete(Config, "/operator-policies/%2F/policy_even", {group, '2xx'}),
|
|
0 = length(http_get(Config, "/operator-policies")),
|
|
0 = length(http_get(Config, "/operator-policies/%2F")),
|
|
unregister_parameters_and_policy_validator(Config),
|
|
passed.
|
|
|
|
disabled_operator_policy_test(Config) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
PolicyPos = #{vhost => <<"/">>,
|
|
name => <<"policy_pos">>,
|
|
pattern => <<".*">>,
|
|
definition => #{testpos => [1,2,3]},
|
|
priority => 10},
|
|
http_put(Config, "/operator-policies/%2F/policy_pos", PolicyPos, ?METHOD_NOT_ALLOWED),
|
|
http_delete(Config, "/operator-policies/%2F/policy_pos", ?METHOD_NOT_ALLOWED),
|
|
0 = length(http_get(Config, "/operator-policies", ?OK)),
|
|
0 = length(http_get(Config, "/operator-policies/%2F", ?OK)),
|
|
unregister_parameters_and_policy_validator(Config),
|
|
passed.
|
|
|
|
policy_test(Config) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
PolicyPos = #{vhost => <<"/">>,
|
|
name => <<"policy_pos">>,
|
|
pattern => <<".*">>,
|
|
definition => #{testpos => [1,2,3]},
|
|
priority => 10},
|
|
PolicyEven = #{vhost => <<"/">>,
|
|
name => <<"policy_even">>,
|
|
pattern => <<".*">>,
|
|
definition => #{testeven => [1,2,3,4]},
|
|
priority => 10},
|
|
http_put(Config,
|
|
"/policies/%2F/policy_pos",
|
|
maps:remove(key, PolicyPos),
|
|
{group, '2xx'}),
|
|
http_put(Config,
|
|
"/policies/%2F/policy_even",
|
|
maps:remove(key, PolicyEven),
|
|
{group, '2xx'}),
|
|
assert_item(PolicyPos, http_get(Config, "/policies/%2F/policy_pos", ?OK)),
|
|
assert_item(PolicyEven, http_get(Config, "/policies/%2F/policy_even", ?OK)),
|
|
List = [PolicyPos, PolicyEven],
|
|
assert_list(List, http_get(Config, "/policies", ?OK)),
|
|
assert_list(List, http_get(Config, "/policies/%2F", ?OK)),
|
|
|
|
http_delete(Config, "/policies/%2F/policy_pos", {group, '2xx'}),
|
|
http_delete(Config, "/policies/%2F/policy_even", {group, '2xx'}),
|
|
0 = length(http_get(Config, "/policies")),
|
|
0 = length(http_get(Config, "/policies/%2F")),
|
|
unregister_parameters_and_policy_validator(Config),
|
|
passed.
|
|
|
|
policy_permissions_test(Config) ->
|
|
register_parameters_and_policy_validator(Config),
|
|
|
|
http_put(Config, "/users/admin", [{password, <<"admin">>},
|
|
{tags, <<"administrator">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/mon", [{password, <<"mon">>},
|
|
{tags, <<"monitoring">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/policy", [{password, <<"policy">>},
|
|
{tags, <<"policymaker">>}], {group, '2xx'}),
|
|
http_put(Config, "/users/mgmt", [{password, <<"mgmt">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
Perms = [{configure, <<".*">>},
|
|
{write, <<".*">>},
|
|
{read, <<".*">>}],
|
|
http_put(Config, "/vhosts/v", none, {group, '2xx'}),
|
|
http_put(Config, "/permissions/v/admin", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/v/mon", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/v/policy", Perms, {group, '2xx'}),
|
|
http_put(Config, "/permissions/v/mgmt", Perms, {group, '2xx'}),
|
|
|
|
Policy = [{pattern, <<".*">>},
|
|
{definition, [{<<"max-length-bytes">>, 3000000}]}],
|
|
Param = [{value, <<"">>}],
|
|
|
|
http_put(Config, "/policies/%2F/HA", Policy, {group, '2xx'}),
|
|
http_put(Config, "/parameters/test/%2F/good", Param, {group, '2xx'}),
|
|
|
|
Pos = fun (U) ->
|
|
http_put(Config, "/policies/v/HA", Policy, U, U, {group, '2xx'}),
|
|
http_put(Config, "/parameters/test/v/good", Param, U, U, {group, '2xx'}),
|
|
http_get(Config, "/policies", U, U, {group, '2xx'}),
|
|
http_get(Config, "/parameters/test", U, U, {group, '2xx'}),
|
|
http_get(Config, "/parameters", U, U, {group, '2xx'}),
|
|
http_get(Config, "/policies/v", U, U, {group, '2xx'}),
|
|
http_get(Config, "/parameters/test/v", U, U, {group, '2xx'}),
|
|
http_get(Config, "/policies/v/HA", U, U, {group, '2xx'}),
|
|
http_get(Config, "/parameters/test/v/good", U, U, {group, '2xx'})
|
|
end,
|
|
Neg = fun (U) ->
|
|
http_put(Config, "/policies/v/HA", Policy, U, U, ?NOT_AUTHORISED),
|
|
http_put(Config,
|
|
"/parameters/test/v/good", Param, U, U, ?NOT_AUTHORISED),
|
|
http_put(Config,
|
|
"/parameters/test/v/admin", Param, U, U, ?NOT_AUTHORISED),
|
|
%% Policies are read-only for management and monitoring.
|
|
http_get(Config, "/policies", U, U, ?OK),
|
|
http_get(Config, "/policies/v", U, U, ?OK),
|
|
http_get(Config, "/parameters", U, U, ?NOT_AUTHORISED),
|
|
http_get(Config, "/parameters/test", U, U, ?NOT_AUTHORISED),
|
|
http_get(Config, "/parameters/test/v", U, U, ?NOT_AUTHORISED),
|
|
http_get(Config, "/policies/v/HA", U, U, ?NOT_AUTHORISED),
|
|
http_get(Config, "/parameters/test/v/good", U, U, ?NOT_AUTHORISED)
|
|
end,
|
|
AlwaysNeg =
|
|
fun (U) ->
|
|
http_put(Config, "/policies/%2F/HA", Policy, U, U, ?NOT_AUTHORISED),
|
|
http_put(Config, "/parameters/test/%2F/good", Param, U, U, ?NOT_AUTHORISED),
|
|
http_get(Config, "/policies/%2F/HA", U, U, ?NOT_AUTHORISED),
|
|
http_get(Config, "/parameters/test/%2F/good", U, U, ?NOT_AUTHORISED)
|
|
end,
|
|
AdminPos =
|
|
fun (U) ->
|
|
http_put(Config, "/policies/%2F/HA", Policy, U, U, {group, '2xx'}),
|
|
http_put(Config, "/parameters/test/%2F/good", Param, U, U, {group, '2xx'}),
|
|
http_get(Config, "/policies/%2F/HA", U, U, {group, '2xx'}),
|
|
http_get(Config, "/parameters/test/%2F/good", U, U, {group, '2xx'})
|
|
end,
|
|
|
|
[Neg(U) || U <- ["mon", "mgmt"]],
|
|
[Pos(U) || U <- ["admin", "policy"]],
|
|
[AlwaysNeg(U) || U <- ["mon", "mgmt", "policy"]],
|
|
[AdminPos(U) || U <- ["admin"]],
|
|
|
|
%% This one is deliberately different between admin and policymaker.
|
|
http_put(Config, "/parameters/test/v/admin", Param, "admin", "admin", {group, '2xx'}),
|
|
http_put(Config, "/parameters/test/v/admin", Param, "policy", "policy",
|
|
?BAD_REQUEST),
|
|
|
|
http_delete(Config, "/vhosts/v", {group, '2xx'}),
|
|
http_delete(Config, "/users/admin", {group, '2xx'}),
|
|
http_delete(Config, "/users/mon", {group, '2xx'}),
|
|
http_delete(Config, "/users/policy", {group, '2xx'}),
|
|
http_delete(Config, "/users/mgmt", {group, '2xx'}),
|
|
http_delete(Config, "/policies/%2F/HA", {group, '2xx'}),
|
|
|
|
unregister_parameters_and_policy_validator(Config),
|
|
passed.
|
|
|
|
issue67_test(Config)->
|
|
{ok, {{_, 401, _}, Headers, _}} = req(Config, get, "/queues",
|
|
[auth_header("user_no_access", "password_no_access")]),
|
|
?assertEqual("application/json",
|
|
proplists:get_value("content-type",Headers)),
|
|
passed.
|
|
|
|
extensions_test(Config) ->
|
|
[#{javascript := <<"dispatcher.js">>}] = http_get(Config, "/extensions", ?OK),
|
|
passed.
|
|
|
|
|
|
cors_test(Config) ->
|
|
%% With CORS disabled. No header should be received.
|
|
R = req(Config, get, "/overview", [auth_header("guest", "guest")]),
|
|
io:format("CORS test R: ~tp~n", [R]),
|
|
{ok, {_, HdNoCORS, _}} = R,
|
|
io:format("CORS test HdNoCORS: ~tp~n", [HdNoCORS]),
|
|
false = lists:keymember("access-control-allow-origin", 1, HdNoCORS),
|
|
%% The Vary header should include "Origin" regardless of CORS configuration.
|
|
{_, "accept, accept-encoding, origin"} = lists:keyfind("vary", 1, HdNoCORS),
|
|
%% Enable CORS.
|
|
rpc(Config, application, set_env, [rabbitmq_management, cors_allow_origins, ["https://rabbitmq.com"]]),
|
|
%% We should only receive allow-origin and allow-credentials from GET.
|
|
{ok, {_, HdGetCORS, _}} = req(Config, get, "/overview",
|
|
[{"origin", "https://rabbitmq.com"}, auth_header("guest", "guest")]),
|
|
true = lists:keymember("access-control-allow-origin", 1, HdGetCORS),
|
|
true = lists:keymember("access-control-allow-credentials", 1, HdGetCORS),
|
|
false = lists:keymember("access-control-expose-headers", 1, HdGetCORS),
|
|
false = lists:keymember("access-control-max-age", 1, HdGetCORS),
|
|
false = lists:keymember("access-control-allow-methods", 1, HdGetCORS),
|
|
false = lists:keymember("access-control-allow-headers", 1, HdGetCORS),
|
|
%% We should receive allow-origin, allow-credentials and allow-methods from OPTIONS.
|
|
{ok, {{_, 200, _}, HdOptionsCORS, _}} = req(Config, options, "/overview",
|
|
[{"origin", "https://rabbitmq.com"}]),
|
|
true = lists:keymember("access-control-allow-origin", 1, HdOptionsCORS),
|
|
true = lists:keymember("access-control-allow-credentials", 1, HdOptionsCORS),
|
|
false = lists:keymember("access-control-expose-headers", 1, HdOptionsCORS),
|
|
true = lists:keymember("access-control-max-age", 1, HdOptionsCORS),
|
|
true = lists:keymember("access-control-allow-methods", 1, HdOptionsCORS),
|
|
false = lists:keymember("access-control-allow-headers", 1, HdOptionsCORS),
|
|
%% We should receive allow-headers when request-headers is sent.
|
|
{ok, {_, HdAllowHeadersCORS, _}} = req(Config, options, "/overview",
|
|
[{"origin", "https://rabbitmq.com"},
|
|
auth_header("guest", "guest"),
|
|
{"access-control-request-headers", "x-piggy-bank"}]),
|
|
{_, "x-piggy-bank"} = lists:keyfind("access-control-allow-headers", 1, HdAllowHeadersCORS),
|
|
%% Disable preflight request caching.
|
|
rpc(Config, application, set_env, [rabbitmq_management, cors_max_age, undefined]),
|
|
%% We shouldn't receive max-age anymore.
|
|
{ok, {_, HdNoMaxAgeCORS, _}} = req(Config, options, "/overview",
|
|
[{"origin", "https://rabbitmq.com"}, auth_header("guest", "guest")]),
|
|
false = lists:keymember("access-control-max-age", 1, HdNoMaxAgeCORS),
|
|
|
|
%% Check OPTIONS method in all paths
|
|
check_cors_all_endpoints(Config),
|
|
%% Disable CORS again.
|
|
rpc(Config, application, set_env, [rabbitmq_management, cors_allow_origins, []]),
|
|
passed.
|
|
|
|
check_cors_all_endpoints(Config) ->
|
|
Endpoints = get_all_http_endpoints(),
|
|
|
|
[begin
|
|
ct:pal("Verifying CORS for module ~tp using an OPTIONS request~n", [EP]),
|
|
{ok, {{_, 200, _}, _, _}} = req(Config, options, EP, [{"origin", "https://rabbitmq.com"}])
|
|
end
|
|
|| EP <- Endpoints].
|
|
|
|
get_all_http_endpoints() ->
|
|
[ Path || {Path, _, _} <- rabbit_mgmt_dispatcher:dispatcher() ].
|
|
|
|
vhost_limits_list_test(Config) ->
|
|
[] = http_get(Config, "/vhost-limits", ?OK),
|
|
|
|
http_get(Config, "/vhost-limits/limit_test_vhost_1", ?NOT_FOUND),
|
|
rabbit_ct_broker_helpers:add_vhost(Config, <<"limit_test_vhost_1">>),
|
|
|
|
[] = http_get(Config, "/vhost-limits/limit_test_vhost_1", ?OK),
|
|
http_get(Config, "/vhost-limits/limit_test_vhost_2", ?NOT_FOUND),
|
|
|
|
rabbit_ct_broker_helpers:add_vhost(Config, <<"limit_test_vhost_2">>),
|
|
|
|
[] = http_get(Config, "/vhost-limits/limit_test_vhost_2", ?OK),
|
|
|
|
Limits1 = [#{vhost => <<"limit_test_vhost_1">>,
|
|
value => #{'max-connections' => 100, 'max-queues' => 100}}],
|
|
Limits2 = [#{vhost => <<"limit_test_vhost_2">>,
|
|
value => #{'max-connections' => 200}}],
|
|
|
|
Expected = Limits1 ++ Limits2,
|
|
|
|
lists:map(
|
|
fun(#{vhost := VHost, value := Val}) ->
|
|
Param = [ {atom_to_binary(K, utf8),V} || {K,V} <- maps:to_list(Val) ],
|
|
ct:pal("Setting limits of virtual host '~ts' to ~tp", [VHost, Param]),
|
|
ok = rabbit_ct_broker_helpers:set_parameter(Config, 0, VHost, <<"vhost-limits">>, <<"limits">>, Param)
|
|
end,
|
|
Expected),
|
|
|
|
?assertEqual(lists:usort(Expected), lists:usort(http_get(Config, "/vhost-limits", ?OK))),
|
|
?assertEqual(Limits1, http_get(Config, "/vhost-limits/limit_test_vhost_1", ?OK)),
|
|
?assertEqual(Limits2, http_get(Config, "/vhost-limits/limit_test_vhost_2", ?OK)),
|
|
|
|
NoVhostUser = <<"no_vhost_user">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, NoVhostUser),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, NoVhostUser, [management]),
|
|
[] = http_get(Config, "/vhost-limits", NoVhostUser, NoVhostUser, ?OK),
|
|
http_get(Config, "/vhost-limits/limit_test_vhost_1", NoVhostUser, NoVhostUser, ?NOT_AUTHORISED),
|
|
http_get(Config, "/vhost-limits/limit_test_vhost_2", NoVhostUser, NoVhostUser, ?NOT_AUTHORISED),
|
|
|
|
Vhost1User = <<"limit_test_vhost_1_user">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, Vhost1User),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, Vhost1User, [management]),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, Vhost1User, <<"limit_test_vhost_1">>),
|
|
Limits1 = http_get(Config, "/vhost-limits", Vhost1User, Vhost1User, ?OK),
|
|
Limits1 = http_get(Config, "/vhost-limits/limit_test_vhost_1", Vhost1User, Vhost1User, ?OK),
|
|
http_get(Config, "/vhost-limits/limit_test_vhost_2", Vhost1User, Vhost1User, ?NOT_AUTHORISED),
|
|
|
|
Vhost2User = <<"limit_test_vhost_2_user">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, Vhost2User),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, Vhost2User, [management]),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, Vhost2User, <<"limit_test_vhost_2">>),
|
|
Limits2 = http_get(Config, "/vhost-limits", Vhost2User, Vhost2User, ?OK),
|
|
http_get(Config, "/vhost-limits/limit_test_vhost_1", Vhost2User, Vhost2User, ?NOT_AUTHORISED),
|
|
Limits2 = http_get(Config, "/vhost-limits/limit_test_vhost_2", Vhost2User, Vhost2User, ?OK).
|
|
|
|
vhost_limit_set_test(Config) ->
|
|
[] = http_get(Config, "/vhost-limits", ?OK),
|
|
rabbit_ct_broker_helpers:add_vhost(Config, <<"limit_test_vhost_1">>),
|
|
[] = http_get(Config, "/vhost-limits/limit_test_vhost_1", ?OK),
|
|
|
|
%% Set a limit
|
|
http_put(Config, "/vhost-limits/limit_test_vhost_1/max-queues", [{value, 100}], ?NO_CONTENT),
|
|
|
|
|
|
Limits_Queues = [#{vhost => <<"limit_test_vhost_1">>,
|
|
value => #{'max-queues' => 100}}],
|
|
|
|
Limits_Queues = http_get(Config, "/vhost-limits/limit_test_vhost_1", ?OK),
|
|
|
|
%% Set another limit
|
|
http_put(Config, "/vhost-limits/limit_test_vhost_1/max-connections", [{value, 200}], ?NO_CONTENT),
|
|
|
|
Limits_Queues_Connections = [#{vhost => <<"limit_test_vhost_1">>,
|
|
value => #{'max-connections' => 200, 'max-queues' => 100}}],
|
|
|
|
Limits_Queues_Connections = http_get(Config, "/vhost-limits/limit_test_vhost_1", ?OK),
|
|
|
|
Limits1 = [#{vhost => <<"limit_test_vhost_1">>,
|
|
value => #{'max-connections' => 200, 'max-queues' => 100}}],
|
|
Limits1 = http_get(Config, "/vhost-limits", ?OK),
|
|
|
|
%% Update a limit
|
|
http_put(Config, "/vhost-limits/limit_test_vhost_1/max-connections", [{value, 1000}], ?NO_CONTENT),
|
|
Limits2 = [#{vhost => <<"limit_test_vhost_1">>,
|
|
value => #{'max-connections' => 1000, 'max-queues' => 100}}],
|
|
Limits2 = http_get(Config, "/vhost-limits/limit_test_vhost_1", ?OK),
|
|
|
|
|
|
Vhost1User = <<"limit_test_vhost_1_user">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, Vhost1User),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, Vhost1User, [management]),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, Vhost1User, <<"limit_test_vhost_1">>),
|
|
|
|
Limits3 = [#{vhost => <<"limit_test_vhost_1">>,
|
|
value => #{'max-connections' => 1000,
|
|
'max-queues' => 100}}],
|
|
Limits3 = http_get(Config, "/vhost-limits/limit_test_vhost_1", Vhost1User, Vhost1User, ?OK),
|
|
|
|
%% Only admin can update limits
|
|
http_put(Config, "/vhost-limits/limit_test_vhost_1/max-connections", [{value, 300}], ?NO_CONTENT),
|
|
|
|
%% Clear a limit
|
|
http_delete(Config, "/vhost-limits/limit_test_vhost_1/max-connections", ?NO_CONTENT),
|
|
Limits4 = [#{vhost => <<"limit_test_vhost_1">>,
|
|
value => #{'max-queues' => 100}}],
|
|
Limits4 = http_get(Config, "/vhost-limits/limit_test_vhost_1", ?OK),
|
|
|
|
%% Only admin can clear limits
|
|
http_delete(Config, "/vhost-limits/limit_test_vhost_1/max-queues", Vhost1User, Vhost1User, ?NOT_AUTHORISED),
|
|
|
|
%% Unknown limit error
|
|
http_put(Config, "/vhost-limits/limit_test_vhost_1/max-channels", [{value, 200}], ?BAD_REQUEST).
|
|
|
|
user_limits_list_test(Config) ->
|
|
?assertEqual([], http_get(Config, "/user-limits", ?OK)),
|
|
|
|
Vhost1 = <<"limit_test_vhost_1">>,
|
|
rabbit_ct_broker_helpers:add_vhost(Config, <<"limit_test_vhost_1">>),
|
|
|
|
http_get(Config, "/user-limits/limit_test_user_1", ?NOT_FOUND),
|
|
|
|
User1 = <<"limit_test_user_1">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, User1),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, User1, [management]),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, User1, Vhost1),
|
|
|
|
?assertEqual([], http_get(Config, "/user-limits/limit_test_user_1", ?OK)),
|
|
http_get(Config, "/user-limits/limit_test_user_2", ?NOT_FOUND),
|
|
|
|
User2 = <<"limit_test_user_2">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, User2),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, User2, [management]),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, User2, Vhost1),
|
|
|
|
?assertEqual([], http_get(Config, "/user-limits/limit_test_user_2", ?OK)),
|
|
|
|
Limits1 = [
|
|
#{
|
|
user => User1,
|
|
value => #{
|
|
'max-connections' => 100,
|
|
'max-channels' => 100
|
|
}
|
|
}],
|
|
Limits2 = [
|
|
#{
|
|
user => User2,
|
|
value => #{
|
|
'max-connections' => 200
|
|
}
|
|
}],
|
|
|
|
Expected = Limits1 ++ Limits2,
|
|
|
|
lists:map(
|
|
fun(#{user := Username, value := Val}) ->
|
|
rabbit_ct_broker_helpers:set_user_limits(Config, 0, Username, Val)
|
|
end,
|
|
Expected),
|
|
|
|
rabbit_ct_helpers:await_condition(
|
|
fun() ->
|
|
Expected =:= http_get(Config, "/user-limits", ?OK)
|
|
end),
|
|
Limits1 = http_get(Config, "/user-limits/limit_test_user_1", ?OK),
|
|
Limits2 = http_get(Config, "/user-limits/limit_test_user_2", ?OK),
|
|
|
|
%% Clear limits and assert
|
|
rabbit_ct_broker_helpers:clear_user_limits(Config, 0, User1,
|
|
<<"max-connections">>),
|
|
|
|
Limits3 = [#{user => User1, value => #{'max-channels' => 100}}],
|
|
?assertEqual(Limits3, http_get(Config, "/user-limits/limit_test_user_1", ?OK)),
|
|
|
|
rabbit_ct_broker_helpers:clear_user_limits(Config, 0, User1,
|
|
<<"max-channels">>),
|
|
?assertEqual([], http_get(Config, "/user-limits/limit_test_user_1", ?OK)),
|
|
|
|
rabbit_ct_broker_helpers:clear_user_limits(Config, 0, <<"limit_test_user_2">>,
|
|
<<"all">>),
|
|
?assertEqual([], http_get(Config, "/user-limits/limit_test_user_2", ?OK)),
|
|
|
|
%% Limit user with no vhost
|
|
NoVhostUser = <<"no_vhost_user">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, NoVhostUser),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, NoVhostUser, [management]),
|
|
|
|
Limits4 = #{
|
|
user => NoVhostUser,
|
|
value => #{
|
|
'max-connections' => 150,
|
|
'max-channels' => 150
|
|
}
|
|
},
|
|
rabbit_ct_broker_helpers:set_user_limits(Config, 0, NoVhostUser, maps:get(value, Limits4)),
|
|
|
|
?assertEqual([Limits4], http_get(Config, "/user-limits/no_vhost_user", ?OK)).
|
|
|
|
user_limit_set_test(Config) ->
|
|
?assertEqual([], http_get(Config, "/user-limits", ?OK)),
|
|
|
|
User1 = <<"limit_test_user_1">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, User1),
|
|
|
|
?assertEqual([], http_get(Config, "/user-limits/limit_test_user_1", ?OK)),
|
|
|
|
%% Set a user limit
|
|
http_put(Config, "/user-limits/limit_test_user_1/max-channels", [{value, 100}], ?NO_CONTENT),
|
|
|
|
MaxChannelsLimit = [#{user => User1, value => #{'max-channels' => 100}}],
|
|
?assertEqual(MaxChannelsLimit, http_get(Config, "/user-limits/limit_test_user_1", ?OK)),
|
|
|
|
%% Set another user limit
|
|
http_put(Config, "/user-limits/limit_test_user_1/max-connections", [{value, 200}], ?NO_CONTENT),
|
|
|
|
MaxConnectionsAndChannelsLimit = [
|
|
#{
|
|
user => User1,
|
|
value => #{
|
|
'max-connections' => 200,
|
|
'max-channels' => 100
|
|
}
|
|
}
|
|
],
|
|
?assertEqual(MaxConnectionsAndChannelsLimit, http_get(Config, "/user-limits/limit_test_user_1", ?OK)),
|
|
|
|
Limits1 = [
|
|
#{
|
|
user => User1,
|
|
value => #{
|
|
'max-connections' => 200,
|
|
'max-channels' => 100
|
|
}
|
|
}],
|
|
?assertEqual(Limits1, http_get(Config, "/user-limits", ?OK)),
|
|
|
|
%% Update a user limit
|
|
http_put(Config, "/user-limits/limit_test_user_1/max-connections", [{value, 1000}], ?NO_CONTENT),
|
|
Limits2 = [
|
|
#{
|
|
user => User1,
|
|
value => #{
|
|
'max-connections' => 1000,
|
|
'max-channels' => 100
|
|
}
|
|
}],
|
|
?assertEqual(Limits2, http_get(Config, "/user-limits/limit_test_user_1", ?OK)),
|
|
|
|
Vhost1 = <<"limit_test_vhost_1">>,
|
|
rabbit_ct_broker_helpers:add_vhost(Config, Vhost1),
|
|
|
|
Vhost1User = <<"limit_test_vhost_1_user">>,
|
|
rabbit_ct_broker_helpers:add_user(Config, Vhost1User),
|
|
rabbit_ct_broker_helpers:set_user_tags(Config, 0, Vhost1User, [management]),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, Vhost1User, Vhost1),
|
|
|
|
Limits3 = [
|
|
#{
|
|
user => User1,
|
|
value => #{
|
|
'max-connections' => 1000,
|
|
'max-channels' => 100
|
|
}
|
|
}],
|
|
?assertEqual(Limits3, http_get(Config, "/user-limits/limit_test_user_1", Vhost1User, Vhost1User, ?OK)),
|
|
|
|
%% Clear a limit
|
|
http_delete(Config, "/user-limits/limit_test_user_1/max-connections", ?NO_CONTENT),
|
|
Limits4 = [#{user => User1, value => #{'max-channels' => 100}}],
|
|
?assertEqual(Limits4, http_get(Config, "/user-limits/limit_test_user_1", ?OK)),
|
|
|
|
%% Only admin can clear limits
|
|
http_delete(Config, "/user-limits/limit_test_user_1/max-channels", Vhost1User, Vhost1User, ?NOT_AUTHORISED),
|
|
|
|
%% Unknown limit error
|
|
http_put(Config, "/user-limits/limit_test_user_1/max-unknown", [{value, 200}], ?BAD_REQUEST).
|
|
|
|
rates_test(Config) ->
|
|
http_put(Config, "/queues/%2F/myqueue", none, {group, '2xx'}),
|
|
{Conn, Ch} = open_connection_and_channel(Config),
|
|
Pid = spawn_link(fun() -> publish(Ch) end),
|
|
|
|
Condition = fun() ->
|
|
Overview = http_get(Config, "/overview"),
|
|
MsgStats = maps:get(message_stats, Overview),
|
|
QueueTotals = maps:get(queue_totals, Overview),
|
|
|
|
maps:get(messages_ready, QueueTotals) > 0 andalso
|
|
maps:get(messages, QueueTotals) > 0 andalso
|
|
maps:get(publish, MsgStats) > 0 andalso
|
|
maps:get(rate, maps:get(publish_details, MsgStats)) > 0 andalso
|
|
maps:get(rate, maps:get(messages_ready_details, QueueTotals)) > 0 andalso
|
|
maps:get(rate, maps:get(messages_details, QueueTotals)) > 0
|
|
end,
|
|
rabbit_ct_helpers:await_condition(Condition, 60000),
|
|
Pid ! stop_publish,
|
|
close_channel(Ch),
|
|
close_connection(Conn),
|
|
http_delete(Config, "/queues/%2F/myqueue", ?NO_CONTENT),
|
|
passed.
|
|
|
|
cli_redirect_test(Config) ->
|
|
assert_permanent_redirect(Config, "cli", "/cli/index.html"),
|
|
passed.
|
|
|
|
api_redirect_test(Config) ->
|
|
assert_permanent_redirect(Config, "api", "/api/index.html"),
|
|
passed.
|
|
|
|
stats_redirect_test(Config) ->
|
|
assert_permanent_redirect(Config, "doc/stats.html", "/api/index.html"),
|
|
passed.
|
|
|
|
oauth_test(Config) ->
|
|
ok = rabbit_ct_broker_helpers:enable_plugin(Config, 0, "rabbitmq_auth_backend_oauth2"),
|
|
|
|
Map1 = http_get(Config, "/auth", ?OK),
|
|
%% Defaults
|
|
?assertEqual(false, maps:get(oauth_enabled, Map1)),
|
|
|
|
%% Misconfiguration
|
|
rpc(Config, application, set_env, [rabbitmq_management, oauth_enabled, true]),
|
|
Map2 = http_get(Config, "/auth", ?OK),
|
|
?assertEqual(false, maps:get(oauth_enabled, Map2)),
|
|
?assertEqual(<<>>, maps:get(oauth_client_id, Map2)),
|
|
?assertEqual(<<>>, maps:get(oauth_provider_url, Map2)),
|
|
%% Valid config requires non empty OAuthClientId, OAuthClientSecret, OAuthResourceId, OAuthProviderUrl
|
|
rpc(Config, application, set_env, [rabbitmq_management, oauth_client_id, "rabbit_user"]),
|
|
rpc(Config, application, set_env, [rabbitmq_management, oauth_client_secret, "rabbit_secret"]),
|
|
rpc(Config, application, set_env, [rabbitmq_management, oauth_provider_url, "http://localhost:8080/uaa"]),
|
|
rpc(Config, application, set_env, [rabbitmq_auth_backend_oauth2, resource_server_id, "rabbitmq"]),
|
|
Map3 = http_get(Config, "/auth", ?OK),
|
|
println(Map3),
|
|
?assertEqual(true, maps:get(oauth_enabled, Map3)),
|
|
?assertEqual(<<"rabbit_user">>, maps:get(oauth_client_id, Map3)),
|
|
?assertEqual(<<"rabbit_secret">>, maps:get(oauth_client_secret, Map3)),
|
|
?assertEqual(<<"rabbitmq">>, maps:get(resource_server_id, Map3)),
|
|
?assertEqual(<<"http://localhost:8080/uaa">>, maps:get(oauth_provider_url, Map3)),
|
|
%% cleanup
|
|
rpc(Config, application, unset_env, [rabbitmq_management, oauth_enabled]).
|
|
|
|
version_test(Config) ->
|
|
ActualVersion = http_get(Config, "/version"),
|
|
ct:log("ActualVersion : ~p", [ActualVersion]),
|
|
ExpectedVersion = rpc(Config, rabbit, base_product_version, []),
|
|
ct:log("ExpectedVersion : ~p", [ExpectedVersion]),
|
|
?assertEqual(ExpectedVersion, binary_to_list(ActualVersion)).
|
|
|
|
login_test(Config) ->
|
|
http_put(Config, "/users/myuser", [{password, <<"myuser">>},
|
|
{tags, <<"management">>}], {group, '2xx'}),
|
|
%% Let's do a post without any other form of authorization
|
|
{ok, {{_, CodeAct, _}, Headers, _}} =
|
|
req(Config, 0, post, "/login",
|
|
[{"content-type", "application/x-www-form-urlencoded"}],
|
|
<<"username=myuser&password=myuser">>),
|
|
?assertEqual(200, CodeAct),
|
|
|
|
%% Extract the authorization header
|
|
Cookie = list_to_binary(proplists:get_value("set-cookie", Headers)),
|
|
[_, Auth] = binary:split(Cookie, <<"=">>, []),
|
|
|
|
%% Request the overview with the auth obtained
|
|
{ok, {{_, CodeAct1, _}, _, _}} =
|
|
req(Config, get, "/overview", [{"Authorization", "Basic " ++ binary_to_list(Auth)}]),
|
|
?assertEqual(200, CodeAct1),
|
|
|
|
%% Let's request a login with an unknown user
|
|
{ok, {{_, CodeAct2, _}, Headers2, _}} =
|
|
req(Config, 0, post, "/login",
|
|
[{"content-type", "application/x-www-form-urlencoded"}],
|
|
<<"username=misteryusernumber1&password=myuser">>),
|
|
?assertEqual(401, CodeAct2),
|
|
?assert(not proplists:is_defined("set-cookie", Headers2)),
|
|
|
|
http_delete(Config, "/users/myuser", {group, '2xx'}),
|
|
passed.
|
|
|
|
csp_headers_test(Config) ->
|
|
AuthHeader = auth_header("guest", "guest"),
|
|
Headers = [{"origin", "https://rabbitmq.com"}, AuthHeader],
|
|
{ok, {_, HdGetCsp0, _}} = req(Config, get, "/whoami", Headers),
|
|
?assert(lists:keymember("content-security-policy", 1, HdGetCsp0)),
|
|
{ok, {_, HdGetCsp1, _}} = req(Config, get_static, "/index.html", Headers),
|
|
?assert(lists:keymember("content-security-policy", 1, HdGetCsp1)).
|
|
|
|
disable_basic_auth_test(Config) ->
|
|
rpc(Config, application, set_env, [rabbitmq_management, disable_basic_auth, true]),
|
|
http_get(Config, "/overview", ?NOT_AUTHORISED),
|
|
|
|
%% Ensure that a request without auth header does not return a basic auth prompt
|
|
OverviewResponseHeaders = http_get_no_auth(Config, "/overview", ?NOT_AUTHORISED),
|
|
?assertEqual(false, lists:keymember("www-authenticate", 1, OverviewResponseHeaders)),
|
|
|
|
http_get(Config, "/nodes", ?NOT_AUTHORISED),
|
|
http_get(Config, "/vhosts", ?NOT_AUTHORISED),
|
|
http_get(Config, "/vhost-limits", ?NOT_AUTHORISED),
|
|
http_put(Config, "/queues/%2F/myqueue", none, ?NOT_AUTHORISED),
|
|
Policy = [{pattern, <<".*">>},
|
|
{definition, [{<<"ha-mode">>, <<"all">>}]}],
|
|
http_put(Config, "/policies/%2F/HA", Policy, ?NOT_AUTHORISED),
|
|
http_delete(Config, "/queues/%2F/myqueue", ?NOT_AUTHORISED),
|
|
http_get(Config, "/definitions", ?NOT_AUTHORISED),
|
|
http_post(Config, "/definitions", [], ?NOT_AUTHORISED),
|
|
rpc(Config, application, set_env, [rabbitmq_management, disable_basic_auth, 50]),
|
|
%% Defaults to 'false' when config is invalid
|
|
http_get(Config, "/overview", ?OK).
|
|
|
|
auth_attempts_test(Config) ->
|
|
rpc(Config, rabbit_core_metrics, reset_auth_attempt_metrics, []),
|
|
{Conn, _Ch} = open_connection_and_channel(Config),
|
|
close_connection(Conn),
|
|
[NodeData] = http_get(Config, "/nodes"),
|
|
Node = binary_to_atom(maps:get(name, NodeData), utf8),
|
|
Map = http_get(Config, "/auth/attempts/" ++ atom_to_list(Node), ?OK),
|
|
Http = get_auth_attempts(<<"http">>, Map),
|
|
Amqp091 = get_auth_attempts(<<"amqp091">>, Map),
|
|
?assertEqual(false, maps:is_key(remote_address, Amqp091)),
|
|
?assertEqual(false, maps:is_key(username, Amqp091)),
|
|
?assertEqual(1, maps:get(auth_attempts, Amqp091)),
|
|
?assertEqual(1, maps:get(auth_attempts_succeeded, Amqp091)),
|
|
?assertEqual(0, maps:get(auth_attempts_failed, Amqp091)),
|
|
?assertEqual(false, maps:is_key(remote_address, Http)),
|
|
?assertEqual(false, maps:is_key(username, Http)),
|
|
?assertEqual(2, maps:get(auth_attempts, Http)),
|
|
?assertEqual(2, maps:get(auth_attempts_succeeded, Http)),
|
|
?assertEqual(0, maps:get(auth_attempts_failed, Http)),
|
|
|
|
rpc(Config, application, set_env, [rabbit, track_auth_attempt_source, true]),
|
|
{Conn2, _Ch2} = open_connection_and_channel(Config),
|
|
close_connection(Conn2),
|
|
Map2 = http_get(Config, "/auth/attempts/" ++ atom_to_list(Node) ++ "/source", ?OK),
|
|
Map3 = http_get(Config, "/auth/attempts/" ++ atom_to_list(Node), ?OK),
|
|
Http2 = get_auth_attempts(<<"http">>, Map2),
|
|
Http3 = get_auth_attempts(<<"http">>, Map3),
|
|
Amqp091_2 = get_auth_attempts(<<"amqp091">>, Map2),
|
|
Amqp091_3 = get_auth_attempts(<<"amqp091">>, Map3),
|
|
?assertEqual(<<"127.0.0.1">>, maps:get(remote_address, Http2)),
|
|
?assertEqual(<<"guest">>, maps:get(username, Http2)),
|
|
?assertEqual(1, maps:get(auth_attempts, Http2)),
|
|
?assertEqual(1, maps:get(auth_attempts_succeeded, Http2)),
|
|
?assertEqual(0, maps:get(auth_attempts_failed, Http2)),
|
|
|
|
?assertEqual(false, maps:is_key(remote_address, Http3)),
|
|
?assertEqual(false, maps:is_key(username, Http3)),
|
|
?assertEqual(4, maps:get(auth_attempts, Http3)),
|
|
?assertEqual(4, maps:get(auth_attempts_succeeded, Http3)),
|
|
?assertEqual(0, maps:get(auth_attempts_failed, Http3)),
|
|
|
|
?assertEqual(true, <<>> =/= maps:get(remote_address, Amqp091_2)),
|
|
?assertEqual(<<"guest">>, maps:get(username, Amqp091_2)),
|
|
?assertEqual(1, maps:get(auth_attempts, Amqp091_2)),
|
|
?assertEqual(1, maps:get(auth_attempts_succeeded, Amqp091_2)),
|
|
?assertEqual(0, maps:get(auth_attempts_failed, Amqp091_2)),
|
|
|
|
?assertEqual(false, maps:is_key(remote_address, Amqp091_3)),
|
|
?assertEqual(false, maps:is_key(username, Amqp091_3)),
|
|
?assertEqual(2, maps:get(auth_attempts, Amqp091_3)),
|
|
?assertEqual(2, maps:get(auth_attempts_succeeded, Amqp091_3)),
|
|
?assertEqual(0, maps:get(auth_attempts_failed, Amqp091_3)),
|
|
|
|
passed.
|
|
|
|
|
|
config_environment_test(Config) ->
|
|
rpc(Config, application, set_env, [rabbitmq_management,
|
|
config_environment_test_env,
|
|
config_environment_test_value]),
|
|
ResultString = http_get_no_decode(Config, "/config/effective",
|
|
"guest", "guest", ?OK),
|
|
CleanString = re:replace(ResultString, "\\s+", "", [global,{return,list}]),
|
|
{ok, Tokens, _} = erl_scan:string(CleanString++"."),
|
|
{ok, AbsForm} = erl_parse:parse_exprs(Tokens),
|
|
{value, EnvList, _} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()),
|
|
V = proplists:get_value(config_environment_test_env,
|
|
proplists:get_value(rabbitmq_management, EnvList)),
|
|
?assertEqual(config_environment_test_value, V).
|
|
|
|
|
|
disabled_qq_replica_opers_test(Config) ->
|
|
Nodename = rabbit_data_coercion:to_list(rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename)),
|
|
Body = [{node, Nodename}],
|
|
http_post(Config, "/queues/quorum/%2F/qq.whatever/replicas/add", Body, ?METHOD_NOT_ALLOWED),
|
|
http_delete(Config, "/queues/quorum/%2F/qq.whatever/replicas/delete", ?METHOD_NOT_ALLOWED, Body),
|
|
http_post(Config, "/queues/quorum/replicas/on/" ++ Nodename ++ "/grow", Body, ?METHOD_NOT_ALLOWED),
|
|
http_delete(Config, "/queues/quorum/replicas/on/" ++ Nodename ++ "/shrink", ?METHOD_NOT_ALLOWED),
|
|
passed.
|
|
|
|
qq_status_test(Config) ->
|
|
QQArgs = [{durable, true}, {arguments, [{'x-queue-type', 'quorum'}]}],
|
|
http_get(Config, "/queues/%2f/qq_status", ?NOT_FOUND),
|
|
http_put(Config, "/queues/%2f/qq_status", QQArgs, {group, '2xx'}),
|
|
[MapRes] = http_get(Config, "/queues/quorum/%2f/qq_status/status", ?OK),
|
|
Keys = ['Commit Index','Last Applied','Last Log Index',
|
|
'Last Written','Machine Version','Membership','Node Name',
|
|
'Raft State','Snapshot Index','Term'],
|
|
?assertEqual(lists:sort(Keys), lists:sort(maps:keys(MapRes))),
|
|
http_delete(Config, "/queues/%2f/qq_status", {group, '2xx'}),
|
|
|
|
|
|
CQArgs = [{durable, true}],
|
|
http_get(Config, "/queues/%2F/cq_status", ?NOT_FOUND),
|
|
http_put(Config, "/queues/%2F/cq_status", CQArgs, {group, '2xx'}),
|
|
ResBody = http_get_no_decode(Config, "/queues/quorum/%2f/cq_status/status", "guest", "guest", 503),
|
|
?assertEqual(#{reason => <<"classic_queue_not_supported">>,
|
|
status => <<"failed">>}, decode_body(ResBody)),
|
|
http_delete(Config, "/queues/%2f/cq_status", {group, '2xx'}),
|
|
passed.
|
|
|
|
|
|
list_deprecated_features_test(Config) ->
|
|
Desc = "This is a deprecated feature",
|
|
DocUrl = "https://rabbitmq.com/",
|
|
FeatureFlags = #{?FUNCTION_NAME =>
|
|
#{provided_by => ?MODULE,
|
|
deprecation_phase => permitted_by_default,
|
|
desc => Desc,
|
|
doc_url => DocUrl}},
|
|
ok = rpc(Config, rabbit_feature_flags, inject_test_feature_flags, [FeatureFlags]),
|
|
Result = http_get(Config, "/deprecated-features", ?OK),
|
|
Features = lists:filter(fun(Map) ->
|
|
maps:get(name, Map) == atom_to_binary(?FUNCTION_NAME)
|
|
end, Result),
|
|
?assertMatch([_], Features),
|
|
[Feature] = Features,
|
|
?assertEqual(<<"permitted_by_default">>, maps:get(deprecation_phase, Feature)),
|
|
?assertEqual(atom_to_binary(?MODULE), maps:get(provided_by, Feature)),
|
|
?assertEqual(list_to_binary(Desc), maps:get(desc, Feature)),
|
|
?assertEqual(list_to_binary(DocUrl), maps:get(doc_url, Feature)).
|
|
|
|
list_used_deprecated_features_test(Config) ->
|
|
Desc = "This is a deprecated feature in use",
|
|
DocUrl = "https://rabbitmq.com/",
|
|
FeatureFlags = #{?FUNCTION_NAME =>
|
|
#{provided_by => ?MODULE,
|
|
deprecation_phase => removed,
|
|
desc => Desc,
|
|
doc_url => DocUrl,
|
|
callbacks => #{is_feature_used => {rabbit_mgmt_wm_deprecated_features, feature_is_used}}}},
|
|
ok = rpc(Config, rabbit_feature_flags, inject_test_feature_flags, [FeatureFlags]),
|
|
Result = http_get(Config, "/deprecated-features/used", ?OK),
|
|
Features = lists:filter(fun(Map) ->
|
|
maps:get(name, Map) == atom_to_binary(?FUNCTION_NAME)
|
|
end, Result),
|
|
?assertMatch([_], Features),
|
|
[Feature] = Features,
|
|
?assertEqual(<<"removed">>, maps:get(deprecation_phase, Feature)),
|
|
?assertEqual(atom_to_binary(?MODULE), maps:get(provided_by, Feature)),
|
|
?assertEqual(list_to_binary(Desc), maps:get(desc, Feature)),
|
|
?assertEqual(list_to_binary(DocUrl), maps:get(doc_url, Feature)).
|
|
|
|
cluster_and_node_tags_test(Config) ->
|
|
Overview = http_get(Config, "/overview"),
|
|
ClusterTags = maps:get(cluster_tags, Overview),
|
|
NodeTags = maps:get(node_tags, Overview),
|
|
ExpectedTags = #{az => <<"us-east-3">>,environment => <<"production">>,
|
|
region => <<"us-east">>},
|
|
?assertEqual(ExpectedTags, ClusterTags),
|
|
?assertEqual(ExpectedTags, NodeTags),
|
|
passed.
|
|
|
|
default_queue_type_fallback_in_overview_test(Config) ->
|
|
Overview = http_get(Config, "/overview"),
|
|
DQT = maps:get(default_queue_type, Overview),
|
|
?assertEqual(<<"classic">>, DQT).
|
|
|
|
default_queue_type_with_value_configured_in_overview_test(Config) ->
|
|
Overview = with_default_queue_type(Config, rabbit_quorum_queue,
|
|
fun(Cfg) ->
|
|
http_get(Cfg, "/overview")
|
|
end),
|
|
|
|
DQT = maps:get(default_queue_type, Overview),
|
|
?assertEqual(<<"quorum">>, DQT).
|
|
|
|
default_queue_types_in_vhost_list_test(Config) ->
|
|
TestName = rabbit_data_coercion:to_binary(?FUNCTION_NAME),
|
|
VHost1 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 1])),
|
|
VHost2 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 2])),
|
|
VHost3 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 3])),
|
|
VHost4 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 4])),
|
|
|
|
VHosts = #{
|
|
VHost1 => undefined,
|
|
VHost2 => <<"classic">>,
|
|
VHost3 => <<"quorum">>,
|
|
VHost4 => <<"stream">>
|
|
},
|
|
|
|
try
|
|
begin
|
|
lists:foreach(
|
|
fun({V, QT}) ->
|
|
rabbit_ct_broker_helpers:add_vhost(Config, V),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, V),
|
|
rabbit_ct_broker_helpers:update_vhost_metadata(Config, V, #{
|
|
default_queue_type => QT
|
|
})
|
|
end, maps:to_list(VHosts)),
|
|
|
|
List = http_get(Config, "/vhosts"),
|
|
ct:pal("GET /api/vhosts returned: ~tp", [List]),
|
|
VH1 = find_map_by_name(VHost1, List),
|
|
?assertEqual(<<"classic">>, maps:get(default_queue_type, VH1)),
|
|
|
|
VH2 = find_map_by_name(VHost2, List),
|
|
?assertEqual(<<"classic">>, maps:get(default_queue_type, VH2)),
|
|
|
|
VH3 = find_map_by_name(VHost3, List),
|
|
?assertEqual(<<"quorum">>, maps:get(default_queue_type, VH3)),
|
|
|
|
VH4 = find_map_by_name(VHost4, List),
|
|
?assertEqual(<<"stream">>, maps:get(default_queue_type, VH4))
|
|
end
|
|
after
|
|
lists:foreach(
|
|
fun(V) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, V)
|
|
end, maps:keys(VHosts))
|
|
end.
|
|
|
|
default_queue_type_of_one_vhost_test(Config) ->
|
|
TestName = rabbit_data_coercion:to_binary(?FUNCTION_NAME),
|
|
VHost1 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 1])),
|
|
VHost2 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 2])),
|
|
VHost3 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 3])),
|
|
VHost4 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 4])),
|
|
|
|
VHosts = #{
|
|
VHost1 => undefined,
|
|
VHost2 => <<"classic">>,
|
|
VHost3 => <<"quorum">>,
|
|
VHost4 => <<"stream">>
|
|
},
|
|
|
|
try
|
|
begin
|
|
lists:foreach(
|
|
fun({V, QT}) ->
|
|
rabbit_ct_broker_helpers:add_vhost(Config, V),
|
|
rabbit_ct_broker_helpers:set_full_permissions(Config, V),
|
|
rabbit_ct_broker_helpers:update_vhost_metadata(Config, V, #{
|
|
default_queue_type => QT
|
|
})
|
|
end, maps:to_list(VHosts)),
|
|
|
|
VH1 = http_get(Config, io_lib:format("/vhosts/~ts", [VHost1])),
|
|
?assertEqual(<<"classic">>, maps:get(default_queue_type, VH1)),
|
|
|
|
VH2 = http_get(Config, io_lib:format("/vhosts/~ts", [VHost2])),
|
|
?assertEqual(<<"classic">>, maps:get(default_queue_type, VH2)),
|
|
|
|
VH3 = http_get(Config, io_lib:format("/vhosts/~ts", [VHost3])),
|
|
?assertEqual(<<"quorum">>, maps:get(default_queue_type, VH3)),
|
|
|
|
VH4 = http_get(Config, io_lib:format("/vhosts/~ts", [VHost4])),
|
|
?assertEqual(<<"stream">>, maps:get(default_queue_type, VH4))
|
|
end
|
|
after
|
|
lists:foreach(
|
|
fun(V) ->
|
|
rabbit_ct_broker_helpers:delete_vhost(Config, V)
|
|
end, maps:keys(VHosts))
|
|
end.
|
|
|
|
%% -------------------------------------------------------------------
|
|
%% Helpers.
|
|
%% -------------------------------------------------------------------
|
|
|
|
%% Finds a map by its <<"name">> key in an HTTP API response.
|
|
-spec find_map_by_name(binary(), [#{binary() => any()}]) -> #{binary() => any()} | undefined.
|
|
find_map_by_name(NameToFind, List) ->
|
|
case lists:filter(fun(#{name := Name}) ->
|
|
Name =:= NameToFind
|
|
end, List) of
|
|
[] -> undefined;
|
|
[Val] -> Val
|
|
end.
|
|
|
|
msg(Key, Headers, Body) ->
|
|
msg(Key, Headers, Body, <<"string">>).
|
|
|
|
msg(Key, Headers, Body, Enc) ->
|
|
#{exchange => <<"">>,
|
|
routing_key => Key,
|
|
properties => #{delivery_mode => 2,
|
|
headers => Headers},
|
|
payload => Body,
|
|
payload_encoding => Enc}.
|
|
|
|
local_port(Conn) ->
|
|
[{sock, Sock}] = amqp_connection:info(Conn, [sock]),
|
|
{ok, Port} = inet:port(Sock),
|
|
Port.
|
|
|
|
spawn_invalid(_Config, 0) ->
|
|
ok;
|
|
spawn_invalid(Config, N) ->
|
|
Self = self(),
|
|
spawn(fun() ->
|
|
timer:sleep(rand:uniform(250)),
|
|
{ok, Sock} = gen_tcp:connect("localhost", amqp_port(Config), [list]),
|
|
ok = gen_tcp:send(Sock, "Some Data"),
|
|
receive_msg(Self)
|
|
end),
|
|
spawn_invalid(Config, N-1).
|
|
|
|
receive_msg(Self) ->
|
|
receive
|
|
{tcp, _, [$A, $M, $Q, $P | _]} ->
|
|
Self ! done
|
|
after
|
|
60000 ->
|
|
Self ! no_reply
|
|
end.
|
|
|
|
wait_for_answers(0) ->
|
|
ok;
|
|
wait_for_answers(N) ->
|
|
receive
|
|
done ->
|
|
wait_for_answers(N-1);
|
|
no_reply ->
|
|
throw(no_reply)
|
|
end.
|
|
|
|
publish(Ch) ->
|
|
amqp_channel:call(Ch, #'basic.publish'{exchange = <<"">>,
|
|
routing_key = <<"myqueue">>},
|
|
#amqp_msg{payload = <<"message">>}),
|
|
receive
|
|
stop_publish ->
|
|
ok
|
|
after 20 ->
|
|
publish(Ch)
|
|
end.
|
|
|
|
%% @doc encode fields and file for HTTP post multipart/form-data.
|
|
%% @reference Inspired by <a href="http://code.activestate.com/recipes/146306/">Python implementation</a>.
|
|
format_multipart_filedata(Boundary, Files) ->
|
|
FileParts = lists:map(fun({FieldName, FileName, FileContent}) ->
|
|
[lists:concat(["--", Boundary]),
|
|
lists:concat(["content-disposition: form-data; name=\"", atom_to_list(FieldName), "\"; filename=\"", FileName, "\""]),
|
|
lists:concat(["content-type: ", "application/octet-stream"]),
|
|
"",
|
|
FileContent]
|
|
end, Files),
|
|
FileParts2 = lists:append(FileParts),
|
|
EndingParts = [lists:concat(["--", Boundary, "--"]), ""],
|
|
Parts = lists:append([FileParts2, EndingParts]),
|
|
string:join(Parts, "\r\n").
|
|
|
|
get_auth_attempts(Protocol, Map) ->
|
|
[A] = lists:filter(fun(#{protocol := P}) ->
|
|
P == Protocol
|
|
end, Map),
|
|
A.
|
|
|
|
await_condition(Fun) ->
|
|
rabbit_ct_helpers:await_condition(
|
|
fun () ->
|
|
try
|
|
Fun()
|
|
catch _:_ ->
|
|
false
|
|
end
|
|
end, ?COLLECT_INTERVAL * 100).
|
|
|
|
|
|
clear_default_queue_type(Config) ->
|
|
rpc(Config, application, unset_env, [rabbit, default_queue_type]).
|
|
|
|
with_default_queue_type(Config, DQT, Fun) ->
|
|
rpc(Config, application, set_env, [rabbit, default_queue_type, DQT]),
|
|
ToReturn = Fun(Config),
|
|
rpc(Config, application, unset_env, [rabbit, default_queue_type]),
|
|
ToReturn.
|