235 lines
8.2 KiB
Erlang
235 lines
8.2 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_tracing_SUITE).
|
|
|
|
-compile(export_all).
|
|
|
|
-define(LOG_DIR, "/var/tmp/rabbitmq-tracing/").
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
-include_lib("amqp_client/include/amqp_client.hrl").
|
|
-include_lib("rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl").
|
|
|
|
all() ->
|
|
[
|
|
{group, non_parallel_tests}
|
|
].
|
|
|
|
groups() ->
|
|
[
|
|
{non_parallel_tests, [], [
|
|
tracing_test,
|
|
tracing_validation_test
|
|
]}
|
|
].
|
|
|
|
%% -------------------------------------------------------------------
|
|
%% Testsuite setup/teardown.
|
|
%% -------------------------------------------------------------------
|
|
|
|
init_per_suite(Config) ->
|
|
inets:start(),
|
|
rabbit_ct_helpers:log_environment(),
|
|
%% initializes httpc
|
|
inets:start(),
|
|
Config1 = rabbit_ct_helpers:set_config(Config, [
|
|
{rmq_nodename_suffix, ?MODULE}
|
|
]),
|
|
rabbit_ct_helpers:run_setup_steps(Config1,
|
|
rabbit_ct_broker_helpers:setup_steps() ++
|
|
rabbit_ct_client_helpers:setup_steps()).
|
|
|
|
end_per_suite(Config) ->
|
|
rabbit_ct_helpers:run_teardown_steps(Config,
|
|
rabbit_ct_client_helpers:teardown_steps() ++
|
|
rabbit_ct_broker_helpers:teardown_steps()).
|
|
|
|
init_per_group(_, Config) ->
|
|
Config.
|
|
|
|
end_per_group(_, Config) ->
|
|
Config.
|
|
|
|
init_per_testcase(Testcase, Config) ->
|
|
rabbit_ct_helpers:testcase_started(Config, Testcase).
|
|
|
|
end_per_testcase(Testcase, Config) ->
|
|
rabbit_ct_helpers:testcase_finished(Config, Testcase).
|
|
|
|
%% -------------------------------------------------------------------
|
|
%% Testcases.
|
|
%% -------------------------------------------------------------------
|
|
|
|
|
|
tracing_test(Config) ->
|
|
case filelib:is_dir(?LOG_DIR) of
|
|
true -> {ok, Files} = file:list_dir(?LOG_DIR),
|
|
[ok = file:delete(?LOG_DIR ++ F) || F <- Files];
|
|
_ -> ok
|
|
end,
|
|
|
|
[] = http_get(Config, "/traces/%2f/"),
|
|
[] = http_get(Config, "/trace-files/"),
|
|
|
|
Args = #{format => <<"json">>,
|
|
pattern => <<"#">>},
|
|
http_put(Config, "/traces/%2f/test", Args, ?CREATED),
|
|
assert_list([#{name => <<"test">>,
|
|
format => <<"json">>,
|
|
pattern => <<"#">>}], http_get(Config, "/traces/%2f/")),
|
|
assert_item(#{name => <<"test">>,
|
|
format => <<"json">>,
|
|
pattern => <<"#">>}, http_get(Config, "/traces/%2f/test")),
|
|
|
|
Ch = rabbit_ct_client_helpers:open_channel(Config),
|
|
amqp_channel:cast(Ch, #'basic.publish'{ exchange = <<"amq.topic">>,
|
|
routing_key = <<"key">> },
|
|
#amqp_msg{props = #'P_basic'{},
|
|
payload = <<"Hello world">>}),
|
|
|
|
rabbit_ct_client_helpers:close_channel(Ch),
|
|
|
|
timer:sleep(100),
|
|
|
|
http_delete(Config, "/traces/%2f/test", ?NO_CONTENT),
|
|
[] = http_get(Config, "/traces/%2f/"),
|
|
assert_list([#{name => <<"test.log">>}], http_get(Config, "/trace-files/")),
|
|
%% This is a bit cheeky as the log is actually one JSON doc per
|
|
%% line and only check the first line
|
|
RawLog = http_get_raw(Config, "/trace-files/test.log"),
|
|
FirstLogLine = hd(string:tokens(RawLog, "\n")),
|
|
FirstLogItem = decode(?OK, [], FirstLogLine),
|
|
assert_item(#{type => <<"published">>,
|
|
exchange => <<"amq.topic">>,
|
|
routing_keys => [<<"key">>],
|
|
payload => base64:encode(<<"Hello world">>)},
|
|
FirstLogItem),
|
|
http_delete(Config, "/trace-files/test.log", ?NO_CONTENT),
|
|
|
|
passed.
|
|
|
|
tracing_validation_test(Config) ->
|
|
Path = "/traces/%2f/test",
|
|
http_put(Config, Path, #{pattern => <<"#">>}, ?BAD_REQUEST),
|
|
http_put(Config, Path, #{format => <<"json">>}, ?BAD_REQUEST),
|
|
http_put(Config, Path, #{format => <<"ebcdic">>,
|
|
pattern => <<"#">>}, ?BAD_REQUEST),
|
|
http_put(Config, Path, #{format => <<"text">>,
|
|
pattern => <<"#">>,
|
|
max_payload_bytes => <<"abc">>}, ?BAD_REQUEST),
|
|
http_put(Config, Path, #{format => <<"json">>,
|
|
pattern => <<"#">>,
|
|
max_payload_bytes => 1000}, ?CREATED),
|
|
http_delete(Config, Path, ?NO_CONTENT),
|
|
ok.
|
|
|
|
%%---------------------------------------------------------------------------
|
|
%% TODO: Below is copied from rabbit_mgmt_test_http,
|
|
%% should be moved to use rabbit_mgmt_test_util once rabbitmq_management
|
|
%% is moved to Common Test
|
|
|
|
http_get(Config, Path) ->
|
|
http_get(Config, Path, ?OK).
|
|
|
|
http_get(Config, Path, CodeExp) ->
|
|
http_get(Config, Path, "guest", "guest", CodeExp).
|
|
|
|
http_get(Config, Path, User, Pass, CodeExp) ->
|
|
{ok, {{_HTTP, CodeAct, _}, Headers, ResBody}} =
|
|
req(Config, get, Path, [auth_header(User, Pass)]),
|
|
assert_code(CodeExp, CodeAct, "GET", Path, ResBody),
|
|
decode(CodeExp, Headers, ResBody).
|
|
|
|
http_get_raw(Config, Path) ->
|
|
http_get_raw(Config, Path, "guest", "guest", ?OK).
|
|
|
|
http_get_raw(Config, Path, User, Pass, CodeExp) ->
|
|
{ok, {{_HTTP, CodeAct, _}, _Headers, ResBody}} =
|
|
req(Config, get, Path, [auth_header(User, Pass)]),
|
|
assert_code(CodeExp, CodeAct, "GET", Path, ResBody),
|
|
ResBody.
|
|
|
|
http_put(Config, Path, List, CodeExp) ->
|
|
http_put_raw(Config, Path, format_for_upload(List), CodeExp).
|
|
|
|
format_for_upload(Map0) ->
|
|
Map = maps:fold(fun(K, V, Acc) ->
|
|
Acc#{atom_to_binary(K, latin1) => V}
|
|
end, #{}, Map0),
|
|
iolist_to_binary(rabbit_json:encode(Map)).
|
|
|
|
http_put_raw(Config, Path, Body, CodeExp) ->
|
|
http_upload_raw(Config, put, Path, Body, "guest", "guest", CodeExp).
|
|
|
|
http_upload_raw(Config, Type, Path, Body, User, Pass, CodeExp) ->
|
|
{ok, {{_HTTP, CodeAct, _}, Headers, ResBody}} =
|
|
req(Config, Type, Path, [auth_header(User, Pass)], Body),
|
|
assert_code(CodeExp, CodeAct, Type, Path, ResBody),
|
|
decode(CodeExp, Headers, ResBody).
|
|
|
|
http_delete(Config, Path, CodeExp) ->
|
|
http_delete(Config, Path, "guest", "guest", CodeExp).
|
|
|
|
http_delete(Config, Path, User, Pass, CodeExp) ->
|
|
{ok, {{_HTTP, CodeAct, _}, Headers, ResBody}} =
|
|
req(Config, delete, Path, [auth_header(User, Pass)]),
|
|
assert_code(CodeExp, CodeAct, "DELETE", Path, ResBody),
|
|
decode(CodeExp, Headers, ResBody).
|
|
|
|
assert_code(CodeExp, CodeAct, Type, Path, Body) ->
|
|
case CodeExp of
|
|
CodeAct -> ok;
|
|
_ -> error({expected, CodeExp, got, CodeAct, type, Type,
|
|
path, Path, body, Body})
|
|
end.
|
|
|
|
mgmt_port(Config) ->
|
|
config_port(Config, tcp_port_mgmt).
|
|
|
|
config_port(Config, PortKey) ->
|
|
rabbit_ct_broker_helpers:get_node_config(Config, 0, PortKey).
|
|
|
|
uri_base_from(Config) ->
|
|
binary_to_list(
|
|
rabbit_mgmt_format:print(
|
|
"http://localhost:~w/api",
|
|
[mgmt_port(Config)])).
|
|
|
|
req(Config, Type, Path, Headers) ->
|
|
httpc:request(Type, {uri_base_from(Config) ++ Path, Headers}, ?HTTPC_OPTS, []).
|
|
|
|
req(Config, Type, Path, Headers, Body) ->
|
|
httpc:request(Type, {uri_base_from(Config) ++ Path, Headers, "application/json", Body},
|
|
?HTTPC_OPTS, []).
|
|
|
|
decode(?OK, _Headers, ResBody) -> cleanup(rabbit_json:decode(rabbit_data_coercion:to_binary(ResBody)));
|
|
decode(_, Headers, _ResBody) -> Headers.
|
|
|
|
cleanup(L) when is_list(L) ->
|
|
[cleanup(I) || I <- L];
|
|
cleanup(M) when is_map(M) ->
|
|
maps:fold(fun(K, V, Acc) ->
|
|
Acc#{binary_to_atom(K, latin1) => cleanup(V)}
|
|
end, #{}, M);
|
|
cleanup(I) ->
|
|
I.
|
|
|
|
auth_header(Username, Password) ->
|
|
{"Authorization",
|
|
"Basic " ++ binary_to_list(base64:encode(Username ++ ":" ++ Password))}.
|
|
|
|
%%---------------------------------------------------------------------------
|
|
|
|
assert_list(Exp, Act) ->
|
|
_ = [assert_item(ExpI, ActI) || {ExpI, ActI} <- lists:zip(Exp, Act)],
|
|
ok.
|
|
|
|
assert_item(ExpI, ActI) ->
|
|
ExpI = maps:with(maps:keys(ExpI), ActI),
|
|
ok.
|