Merge pull request #14114 from rabbitmq/otp28

Improve Erlang/OTP 28 compatibility, use OTP 28 in the pipelines
This commit is contained in:
Michael Klishin 2025-06-27 21:16:39 +04:00 committed by GitHub
commit f55377575a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 295 additions and 254 deletions

View File

@ -20,7 +20,7 @@ on:
# a tag of the erlang image, see https://hub.docker.com/_/erlang for available tags # a tag of the erlang image, see https://hub.docker.com/_/erlang for available tags
# also used in the setup-beam step (same tag should work for both) # also used in the setup-beam step (same tag should work for both)
description: OTP version (eg. `26`, `26.2.5.6`) description: OTP version (eg. `26`, `26.2.5.6`)
default: 27 default: 28
build_arm: build_arm:
description: Build for ARM64 as well? description: Build for ARM64 as well?
type: boolean type: boolean
@ -36,7 +36,7 @@ jobs:
strategy: strategy:
matrix: matrix:
otp_version: otp_version:
- ${{ github.event.inputs.otp_version || '27' }} - ${{ github.event.inputs.otp_version || '28' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
# When dependabot, or a user from a fork, creates PRs, secrets are not injected, and the OCI workflow can't push the image # When dependabot, or a user from a fork, creates PRs, secrets are not injected, and the OCI workflow can't push the image
@ -76,7 +76,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
otp_version: otp_version:
- ${{ github.event.inputs.otp_version || '27' }} - ${{ github.event.inputs.otp_version || '28' }}
needs: build-package-generic-unix needs: build-package-generic-unix
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ needs.build-package-generic-unix.outputs.authorized }} == 'true' if: ${{ needs.build-package-generic-unix.outputs.authorized }} == 'true'

View File

@ -13,7 +13,7 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }} group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true cancel-in-progress: true
env: env:
OTP_VERSION: "27" OTP_VERSION: "28"
jobs: jobs:
peer-discovery-aws-integration-test: peer-discovery-aws-integration-test:
name: Integration Test name: Integration Test
@ -48,10 +48,10 @@ jobs:
polling-seconds: 60 polling-seconds: 60
- name: CONFIGURE OTP & ELIXIR - name: CONFIGURE OTP & ELIXIR
if: steps.authorized.outputs.authorized == 'true' if: steps.authorized.outputs.authorized == 'true'
uses: erlef/setup-beam@v1.17 uses: erlef/setup-beam@v1
with: with:
otp-version: ${{ env.OTP_VERSION }} otp-version: ${{ env.OTP_VERSION }}
elixir-version: "1.17" elixir-version: "1.18"
- name: SETUP ecs-cli - name: SETUP ecs-cli
if: steps.authorized.outputs.authorized == 'true' if: steps.authorized.outputs.authorized == 'true'
env: env:

View File

@ -44,7 +44,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Configure OTP & Elixir - name: Configure OTP & Elixir
uses: erlef/setup-beam@v1.17 uses: erlef/setup-beam@v1
with: with:
otp-version: ${{ matrix.erlang_version }} otp-version: ${{ matrix.erlang_version }}
elixir-version: ${{ matrix.elixir_version }} elixir-version: ${{ matrix.elixir_version }}

View File

@ -33,8 +33,13 @@ jobs:
- name: FETCH TAGS - name: FETCH TAGS
run: git fetch --tags run: git fetch --tags
- name: EXTRACT ACTIVEMQ VERSION
if: inputs.plugin == 'amqp10_client'
run: |
awk '/^ACTIVEMQ_VERSION/ {print $1 "=" $3}' deps/amqp10_client/Makefile >> $GITHUB_ENV
- name: SETUP OTP & ELIXIR - name: SETUP OTP & ELIXIR
uses: erlef/setup-beam@v1.17 uses: erlef/setup-beam@v1
with: with:
otp-version: ${{ inputs.erlang_version }} otp-version: ${{ inputs.erlang_version }}
elixir-version: ${{ inputs.elixir_version }} elixir-version: ${{ inputs.elixir_version }}
@ -99,12 +104,27 @@ jobs:
docker run -d --network host --name erlang_low_version erlang:${LOW_ERLANG_VERSION} \ docker run -d --network host --name erlang_low_version erlang:${LOW_ERLANG_VERSION} \
erl -sname rabbit_fifo_prop@localhost -setcookie $(cat ~/.erlang.cookie) -noinput erl -sname rabbit_fifo_prop@localhost -setcookie $(cat ~/.erlang.cookie) -noinput
- name: RESTORE ACTIVEMQ FROM CACHE
if: inputs.plugin == 'amqp10_client'
uses: actions/cache/restore@v4
id: cache-activemq-restore
with:
path: deps/amqp10_client/test/system_SUITE_data/apache-activemq-${{ env.ACTIVEMQ_VERSION }}-bin.tar.gz
key: activemq-${{ env.ACTIVEMQ_VERSION }}
- name: RUN TESTS - name: RUN TESTS
if: inputs.plugin != 'rabbitmq_cli' if: inputs.plugin != 'rabbitmq_cli'
run: | run: |
sudo netstat -ntp sudo netstat -ntp
make -C deps/${{ inputs.plugin }} ${{ inputs.make_target }} RABBITMQ_METADATA_STORE=${{ inputs.metadata_store }} make -C deps/${{ inputs.plugin }} ${{ inputs.make_target }} RABBITMQ_METADATA_STORE=${{ inputs.metadata_store }}
- name: CACHE ACTIVEMQ
uses: actions/cache/save@v4
if: inputs.plugin == 'amqp10_client' && steps.cache-activemq-restore.outputs.cache-hit != 'true'
with:
path: deps/amqp10_client/test/system_SUITE_data/apache-activemq-${{ env.ACTIVEMQ_VERSION }}-bin.tar.gz
key: activemq-${{ env.ACTIVEMQ_VERSION }}
# rabbitmq_cli needs a correct broker version for two of its tests. # rabbitmq_cli needs a correct broker version for two of its tests.
# But setting PROJECT_VERSION makes other plugins fail. # But setting PROJECT_VERSION makes other plugins fail.
- name: RUN TESTS (rabbitmq_cli) - name: RUN TESTS (rabbitmq_cli)

View File

@ -17,7 +17,7 @@ jobs:
plugin: plugin:
# These are using plugin-specific test jobs. # These are using plugin-specific test jobs.
- rabbit - rabbit
- rabbitmq_mqtt # - rabbitmq_mqtt # disabled due to Elixir 1.18 JSON conficts
- rabbitmq_peer_discovery_aws - rabbitmq_peer_discovery_aws
# These are from the test-plugin test job. # These are from the test-plugin test job.
- amqp10_client - amqp10_client
@ -57,14 +57,14 @@ jobs:
- rabbitmq_shovel - rabbitmq_shovel
- rabbitmq_shovel_management - rabbitmq_shovel_management
- rabbitmq_shovel_prometheus - rabbitmq_shovel_prometheus
- rabbitmq_stomp # - rabbitmq_stomp # disabled due to Elixir 1.18 JSON conficts
- rabbitmq_stream # - rabbitmq_stream # disabled due to Elixir 1.18 JSON conficts
- rabbitmq_stream_common - rabbitmq_stream_common
- rabbitmq_stream_management - rabbitmq_stream_management
- rabbitmq_tracing - rabbitmq_tracing
- rabbitmq_trust_store - rabbitmq_trust_store
- rabbitmq_web_dispatch - rabbitmq_web_dispatch
- rabbitmq_web_mqtt # - rabbitmq_web_mqtt # disabled due to Elixir 1.18 JSON conficts
- rabbitmq_web_stomp - rabbitmq_web_stomp
# This one we do not want to run tests so no corresponding test job. # This one we do not want to run tests so no corresponding test job.
- rabbitmq_ct_helpers - rabbitmq_ct_helpers

View File

@ -23,8 +23,9 @@ jobs:
erlang_version: erlang_version:
- '26' - '26'
- '27' - '27'
- '28'
elixir_version: elixir_version:
- '1.17' - '1.18'
# @todo Add macOS and Windows. # @todo Add macOS and Windows.
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60 timeout-minutes: 60
@ -36,7 +37,7 @@ jobs:
run: git fetch --tags run: git fetch --tags
- name: SETUP OTP & ELIXIR - name: SETUP OTP & ELIXIR
uses: erlef/setup-beam@v1.17 uses: erlef/setup-beam@v1.19
with: with:
otp-version: ${{ matrix.erlang_version }} otp-version: ${{ matrix.erlang_version }}
elixir-version: ${{ matrix.elixir_version }} elixir-version: ${{ matrix.elixir_version }}
@ -62,9 +63,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
erlang_version: erlang_version:
- '27' - '28'
elixir_version: elixir_version:
- '1.17' - '1.18'
metadata_store: metadata_store:
- mnesia - mnesia
- khepri - khepri
@ -81,9 +82,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
erlang_version: erlang_version:
- '27' - '28'
elixir_version: elixir_version:
- '1.17' - '1.18'
metadata_store: metadata_store:
- mnesia - mnesia
- khepri - khepri
@ -100,9 +101,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
erlang_version: # Latest OTP erlang_version: # Latest OTP
- '27' - '28'
elixir_version: # Latest Elixir elixir_version: # Latest Elixir
- '1.17' - '1.18'
uses: ./.github/workflows/test-make-type-check.yaml uses: ./.github/workflows/test-make-type-check.yaml
with: with:
erlang_version: ${{ matrix.erlang_version }} erlang_version: ${{ matrix.erlang_version }}

View File

@ -32,7 +32,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Configure OTP & Elixir - name: Configure OTP & Elixir
uses: erlef/setup-beam@v1.17 uses: erlef/setup-beam@v1
with: with:
otp-version: ${{ matrix.erlang_version }} otp-version: ${{ matrix.erlang_version }}
elixir-version: ${{ matrix.elixir_version }} elixir-version: ${{ matrix.elixir_version }}

View File

@ -36,7 +36,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Configure OTP & Elixir - name: Configure OTP & Elixir
uses: erlef/setup-beam@v1.17 uses: erlef/setup-beam@v1
with: with:
otp-version: ${{ matrix.erlang_version }} otp-version: ${{ matrix.erlang_version }}
elixir-version: ${{ matrix.elixir_version }} elixir-version: ${{ matrix.elixir_version }}

View File

@ -610,13 +610,13 @@ ranch_handshake(Ref) ->
tune_buffer_size(Sock, dynamic_buffer) -> tune_buffer_size(Sock, dynamic_buffer) ->
case rabbit_net:setopts(Sock, [{buffer, 128}]) of case rabbit_net:setopts(Sock, [{buffer, 128}]) of
ok -> ok; ok -> ok;
{error, _} -> rabbit_net:fast_close(Sock), {error, _} -> _ = rabbit_net:fast_close(Sock),
exit(normal) exit(normal)
end; end;
tune_buffer_size(Sock, static_buffer) -> tune_buffer_size(Sock, static_buffer) ->
case tune_buffer_size_static(Sock) of case tune_buffer_size_static(Sock) of
ok -> ok; ok -> ok;
{error, _} -> rabbit_net:fast_close(Sock), {error, _} -> _ = rabbit_net:fast_close(Sock),
exit(normal) exit(normal)
end. end.

View File

@ -275,7 +275,7 @@ socket_op(Sock, Fun) ->
case Fun(Sock) of case Fun(Sock) of
{ok, Res} -> Res; {ok, Res} -> Res;
{error, Reason} -> socket_error(Reason), {error, Reason} -> socket_error(Reason),
rabbit_net:fast_close(RealSocket), _ = rabbit_net:fast_close(RealSocket),
exit(normal) exit(normal)
end. end.
@ -287,10 +287,10 @@ start_connection(Parent, HelperSups, RanchRef, Deb, Sock) ->
RealSocket = rabbit_net:unwrap_socket(Sock), RealSocket = rabbit_net:unwrap_socket(Sock),
Name = case rabbit_net:connection_string(Sock, inbound) of Name = case rabbit_net:connection_string(Sock, inbound) of
{ok, Str} -> list_to_binary(Str); {ok, Str} -> list_to_binary(Str);
{error, enotconn} -> rabbit_net:fast_close(RealSocket), {error, enotconn} -> _ = rabbit_net:fast_close(RealSocket),
exit(normal); exit(normal);
{error, Reason} -> socket_error(Reason), {error, Reason} -> socket_error(Reason),
rabbit_net:fast_close(RealSocket), _ = rabbit_net:fast_close(RealSocket),
exit(normal) exit(normal)
end, end,
{ok, HandshakeTimeout} = application:get_env(rabbit, handshake_timeout), {ok, HandshakeTimeout} = application:get_env(rabbit, handshake_timeout),
@ -364,7 +364,7 @@ start_connection(Parent, HelperSups, RanchRef, Deb, Sock) ->
%% We don't call gen_tcp:close/1 here since it waits for %% We don't call gen_tcp:close/1 here since it waits for
%% pending output to be sent, which results in unnecessary %% pending output to be sent, which results in unnecessary
%% delays. %% delays.
rabbit_net:fast_close(RealSocket), _ = rabbit_net:fast_close(RealSocket),
rabbit_networking:unregister_connection(self()), rabbit_networking:unregister_connection(self()),
rabbit_core_metrics:connection_closed(self()), rabbit_core_metrics:connection_closed(self()),
ClientProperties = case get(client_properties) of ClientProperties = case get(client_properties) of

View File

@ -100,60 +100,64 @@ end_per_testcase(Testcase, Config) ->
%% Test cases. %% Test cases.
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
single_node_list_of_user(Config) -> single_node_list_of_user(Config) ->
Username = proplists:get_value(rmq_username, Config), Username1 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-1"),
Username2 = <<"guest2">>, Username2 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-2"),
Vhost = proplists:get_value(rmq_vhost, Config), Vhost = proplists:get_value(rmq_vhost, Config),
rabbit_ct_broker_helpers:add_user(Config, Username2), [ begin
rabbit_ct_broker_helpers:set_full_permissions(Config, Username2, Vhost), rabbit_ct_broker_helpers:add_user(Config, U),
rabbit_ct_broker_helpers:set_full_permissions(Config, U, Vhost)
end || U <- [Username1, Username2]],
?assertEqual(0, count_connections_in(Config, Username)), ?assertEqual(0, count_connections_in(Config, Username1)),
?assertEqual(0, count_connections_in(Config, Username2)), ?assertEqual(0, count_connections_in(Config, Username2)),
[Conn1] = open_connections(Config, [0]), [Conn1] = open_connections(Config, [{0, Username1}]),
?awaitMatch(1, count_connections_in(Config, Username), ?AWAIT_TIMEOUT), ?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[#tracked_connection{username = Username}] = connections_in(Config, Username), [#tracked_connection{username = Username1}] = connections_in(Config, Username1),
close_connections([Conn1]), close_connections([Conn1]),
?awaitMatch(0, count_connections_in(Config, Username), ?AWAIT_TIMEOUT), ?awaitMatch(0, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[Conn2] = open_connections(Config, [{0, Username2}]), [Conn2] = open_connections(Config, [{0, Username2}]),
?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT), ?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
[#tracked_connection{username = Username2}] = connections_in(Config, Username2), [#tracked_connection{username = Username2}] = connections_in(Config, Username2),
[Conn3] = open_connections(Config, [0]), [Conn3] = open_connections(Config, [{0, Username1}]),
?awaitMatch(1, count_connections_in(Config, Username), ?AWAIT_TIMEOUT), ?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[#tracked_connection{username = Username}] = connections_in(Config, Username), [#tracked_connection{username = Username1}] = connections_in(Config, Username1),
[Conn4] = open_connections(Config, [0]), [Conn4] = open_connections(Config, [{0, Username1}]),
kill_connections([Conn4]), kill_connections([Conn4]),
?awaitMatch(1, count_connections_in(Config, Username), ?AWAIT_TIMEOUT), ?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[#tracked_connection{username = Username}] = connections_in(Config, Username), [#tracked_connection{username = Username1}] = connections_in(Config, Username1),
[Conn5] = open_connections(Config, [0]), [Conn5] = open_connections(Config, [{0, Username1}]),
?awaitMatch(2, count_connections_in(Config, Username), ?AWAIT_TIMEOUT), ?awaitMatch(2, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[Username, Username] = [Username1, Username1] =
lists:map(fun (#tracked_connection{username = U}) -> U end, lists:map(fun (#tracked_connection{username = U}) -> U end,
connections_in(Config, Username)), connections_in(Config, Username1)),
close_connections([Conn2, Conn3, Conn5]), close_connections([Conn2, Conn3, Conn5]),
rabbit_ct_broker_helpers:delete_user(Config, Username2), rabbit_ct_broker_helpers:delete_user(Config, Username2),
?awaitMatch(0, length(all_connections(Config)), ?AWAIT_TIMEOUT). ?awaitMatch(0, length(all_connections(Config)), ?AWAIT_TIMEOUT).
single_node_user_deletion_forces_connection_closure(Config) -> single_node_user_deletion_forces_connection_closure(Config) ->
Username = proplists:get_value(rmq_username, Config), Username1 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-1"),
Username2 = <<"guest2">>, Username2 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-2"),
Vhost = proplists:get_value(rmq_vhost, Config), Vhost = proplists:get_value(rmq_vhost, Config),
rabbit_ct_broker_helpers:add_user(Config, Username2), [ begin
rabbit_ct_broker_helpers:set_full_permissions(Config, Username2, Vhost), rabbit_ct_broker_helpers:add_user(Config, U),
rabbit_ct_broker_helpers:set_full_permissions(Config, U, Vhost)
end || U <- [Username1, Username2]],
?assertEqual(0, count_connections_in(Config, Username)), ?assertEqual(0, count_connections_in(Config, Username1)),
?assertEqual(0, count_connections_in(Config, Username2)), ?assertEqual(0, count_connections_in(Config, Username2)),
[Conn1] = open_connections(Config, [0]), [Conn1] = open_connections(Config, [{0, Username1}]),
?awaitMatch(1, count_connections_in(Config, Username), ?AWAIT_TIMEOUT), ?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[_Conn2] = open_connections(Config, [{0, Username2}]), [_Conn2] = open_connections(Config, [{0, Username2}]),
?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT), ?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
@ -162,22 +166,24 @@ single_node_user_deletion_forces_connection_closure(Config) ->
?awaitMatch(0, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT), ?awaitMatch(0, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
close_connections([Conn1]), close_connections([Conn1]),
?awaitMatch(0, count_connections_in(Config, Username), ?AWAIT_TIMEOUT). ?awaitMatch(0, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT).
cluster_user_deletion_forces_connection_closure(Config) -> cluster_user_deletion_forces_connection_closure(Config) ->
Username = proplists:get_value(rmq_username, Config), Username1 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-1"),
Username2 = <<"guest2">>, Username2 = list_to_binary(atom_to_list(?FUNCTION_NAME) ++ "-2"),
Vhost = proplists:get_value(rmq_vhost, Config), Vhost = proplists:get_value(rmq_vhost, Config),
rabbit_ct_broker_helpers:add_user(Config, Username2), [ begin
rabbit_ct_broker_helpers:set_full_permissions(Config, Username2, Vhost), rabbit_ct_broker_helpers:add_user(Config, U),
rabbit_ct_broker_helpers:set_full_permissions(Config, U, Vhost)
end || U <- [Username1, Username2]],
?assertEqual(0, count_connections_in(Config, Username)), ?assertEqual(0, count_connections_in(Config, Username1)),
?assertEqual(0, count_connections_in(Config, Username2)), ?assertEqual(0, count_connections_in(Config, Username2)),
[Conn1] = open_connections(Config, [{0, Username}]), [Conn1] = open_connections(Config, [{0, Username1}]),
?awaitMatch(1, count_connections_in(Config, Username), ?AWAIT_TIMEOUT), ?awaitMatch(1, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT),
[_Conn2] = open_connections(Config, [{1, Username2}]), [_Conn2] = open_connections(Config, [{1, Username2}]),
?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT), ?awaitMatch(1, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
@ -186,7 +192,7 @@ cluster_user_deletion_forces_connection_closure(Config) ->
?awaitMatch(0, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT), ?awaitMatch(0, count_connections_in(Config, Username2), ?AWAIT_TIMEOUT),
close_connections([Conn1]), close_connections([Conn1]),
?awaitMatch(0, count_connections_in(Config, Username), ?AWAIT_TIMEOUT). ?awaitMatch(0, count_connections_in(Config, Username1), ?AWAIT_TIMEOUT).
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
%% Helpers %% Helpers

View File

@ -1285,38 +1285,43 @@ single_active_consumer_priority(Config) ->
ok. ok.
force_shrink_member_to_current_member(Config) -> force_shrink_member_to_current_member(Config) ->
[Server0, Server1, Server2] = case rabbit_ct_helpers:is_mixed_versions() of
rabbit_ct_broker_helpers:get_node_configs(Config, nodename), true ->
{skip, "Should not run in mixed version environments"};
_ ->
[Server0, Server1, Server2] =
rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
Ch = rabbit_ct_client_helpers:open_channel(Config, Server0), Ch = rabbit_ct_client_helpers:open_channel(Config, Server0),
QQ = ?config(queue_name, Config), QQ = ?config(queue_name, Config),
?assertEqual({'queue.declare_ok', QQ, 0, 0}, ?assertEqual({'queue.declare_ok', QQ, 0, 0},
declare(Ch, QQ, [{<<"x-queue-type">>, longstr, <<"quorum">>}])), declare(Ch, QQ, [{<<"x-queue-type">>, longstr, <<"quorum">>}])),
RaName = ra_name(QQ), RaName = ra_name(QQ),
rabbit_ct_client_helpers:publish(Ch, QQ, 3), rabbit_ct_client_helpers:publish(Ch, QQ, 3),
wait_for_messages_ready([Server0], RaName, 3), wait_for_messages_ready([Server0], RaName, 3),
{ok, Q0} = rpc:call(Server0, rabbit_amqqueue, lookup, [QQ, <<"/">>]), {ok, Q0} = rpc:call(Server0, rabbit_amqqueue, lookup, [QQ, <<"/">>]),
#{nodes := Nodes0} = amqqueue:get_type_state(Q0), #{nodes := Nodes0} = amqqueue:get_type_state(Q0),
?assertEqual(3, length(Nodes0)), ?assertEqual(3, length(Nodes0)),
rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_quorum_queue, rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_quorum_queue,
force_shrink_member_to_current_member, [<<"/">>, QQ]), force_shrink_member_to_current_member, [<<"/">>, QQ]),
wait_for_messages_ready([Server0], RaName, 3), wait_for_messages_ready([Server0], RaName, 3),
{ok, Q1} = rpc:call(Server0, rabbit_amqqueue, lookup, [QQ, <<"/">>]), {ok, Q1} = rpc:call(Server0, rabbit_amqqueue, lookup, [QQ, <<"/">>]),
#{nodes := Nodes1} = amqqueue:get_type_state(Q1), #{nodes := Nodes1} = amqqueue:get_type_state(Q1),
?assertEqual(1, length(Nodes1)), ?assertEqual(1, length(Nodes1)),
%% grow queues back to all nodes %% grow queues back to all nodes
[rpc:call(Server0, rabbit_quorum_queue, grow, [S, <<"/">>, <<".*">>, all]) || S <- [Server1, Server2]], [rpc:call(Server0, rabbit_quorum_queue, grow, [S, <<"/">>, <<".*">>, all]) || S <- [Server1, Server2]],
wait_for_messages_ready([Server0], RaName, 3), wait_for_messages_ready([Server0], RaName, 3),
{ok, Q2} = rpc:call(Server0, rabbit_amqqueue, lookup, [QQ, <<"/">>]), {ok, Q2} = rpc:call(Server0, rabbit_amqqueue, lookup, [QQ, <<"/">>]),
#{nodes := Nodes2} = amqqueue:get_type_state(Q2), #{nodes := Nodes2} = amqqueue:get_type_state(Q2),
?assertEqual(3, length(Nodes2)). ?assertEqual(3, length(Nodes2))
end.
force_all_queues_shrink_member_to_current_member(Config) -> force_all_queues_shrink_member_to_current_member(Config) ->
[Server0, Server1, Server2] = [Server0, Server1, Server2] =

View File

@ -1111,32 +1111,43 @@ two_nodes_same_otp_version(Config0) ->
%% Run the log on two Erlang nodes with different OTP versions. %% Run the log on two Erlang nodes with different OTP versions.
two_nodes_different_otp_version(_Config) -> two_nodes_different_otp_version(_Config) ->
Node = 'rabbit_fifo_prop@localhost', case erlang:system_info(otp_release) of
case net_adm:ping(Node) of "28" ->
pong -> %% Compiling a BEAM file on OTP 28 and loading it on OTP 26 or 27
case is_same_otp_version(Node) of %% causes a "corrupt atom table" error.
true -> %% https://github.com/erlang/otp/pull/8913#issue-2572291638
ct:fail("expected CT node and 'rabbit_fifo_prop@localhost' " {skip, "loading BEAM file compiled on OTP 28 on a lower OTP version is unsupported"};
"to have different OTP versions"); _ ->
false -> Node = 'rabbit_fifo_prop@localhost',
Prefixes = ["rabbit_fifo", "rabbit_misc", "mc", case net_adm:ping(Node) of
"lqueue", "priority_queue", "ra_"], pong ->
[begin case is_same_otp_version(Node) of
Mod = list_to_atom(ModStr), true ->
{Mod, Bin, _File} = code:get_object_code(Mod), ct:fail("expected CT node and 'rabbit_fifo_prop@localhost' "
{module, Mod} = erpc:call(Node, code, load_binary, [Mod, ModStr, Bin]) "to have different OTP versions");
end false ->
|| {ModStr, _FileName, _Loaded} <- code:all_available(), Prefixes = ["rabbit_fifo", "rabbit_misc", "mc",
lists:any(fun(Prefix) -> lists:prefix(Prefix, ModStr) end, Prefixes)], "lqueue", "priority_queue", "ra_"],
two_nodes(Node) [begin
end; Mod = list_to_atom(ModStr),
pang -> {Mod, Bin, _File} = code:get_object_code(Mod),
Reason = {node_down, Node}, {module, Mod} = erpc:call(Node, code, load_binary,
case rabbit_ct_helpers:is_ci() of [Mod, ModStr, Bin])
true -> end
ct:fail(Reason); || {ModStr, _FileName, _Loaded} <- code:all_available(),
false -> lists:any(fun(Prefix) ->
{skip, Reason} lists:prefix(Prefix, ModStr)
end, Prefixes)],
two_nodes(Node)
end;
pang ->
Reason = {node_down, Node},
case rabbit_ct_helpers:is_ci() of
true ->
ct:fail(Reason);
false ->
{skip, Reason}
end
end end
end. end.

View File

@ -43,7 +43,7 @@ DEP_EARLY_PLUGINS = $(PROJECT)/mk/rabbitmq-early-plugin.mk
DEP_PLUGINS = $(PROJECT)/mk/rabbitmq-build.mk \ DEP_PLUGINS = $(PROJECT)/mk/rabbitmq-build.mk \
$(PROJECT)/mk/rabbitmq-hexpm.mk $(PROJECT)/mk/rabbitmq-hexpm.mk
PLT_APPS += mnesia crypto ssl PLT_APPS += mnesia crypto ssl xmerl
include ../../rabbitmq-components.mk include ../../rabbitmq-components.mk
include ../../erlang.mk include ../../erlang.mk

View File

@ -109,11 +109,18 @@ find_by_type(Type, {rdnSequence, RDNs}) ->
%% Formatting functions %% Formatting functions
%%-------------------------------------------------------------------------- %%--------------------------------------------------------------------------
-if (?OTP_RELEASE >= 28).
-define(M, 'PKIXAlgs-2009').
-else.
-define(M, 'OTP-PUB-KEY').
-endif.
sanitize_other_name(Bin) when is_binary(Bin) -> sanitize_other_name(Bin) when is_binary(Bin) ->
%% We make a wild assumption about the types here %% We make a wild assumption about the types here
%% but ASN.1 decoding functions in OTP only offer so much and SAN values %% but ASN.1 decoding functions in OTP only offer so much and SAN values
%% are expected to be "string-like" by RabbitMQ %% are expected to be "string-like" by RabbitMQ
case 'OTP-PUB-KEY':decode('DirectoryString', Bin) of case ?M:decode('DirectoryString', Bin) of
{ok, {_, Val}} -> Val; {ok, {_, Val}} -> Val;
Other -> Other Other -> Other
end. end.

View File

@ -82,19 +82,10 @@
-define(SSL_CLOSE_TIMEOUT, 5000). -define(SSL_CLOSE_TIMEOUT, 5000).
-define(IS_SSL(Sock), is_tuple(Sock) -define(IS_SSL(Sock), is_tuple(Sock)
andalso (tuple_size(Sock) =:= 3)
andalso (element(1, Sock) =:= sslsocket)). andalso (element(1, Sock) =:= sslsocket)).
is_ssl(Sock) -> ?IS_SSL(Sock). is_ssl(Sock) -> ?IS_SSL(Sock).
%% Seems hackish. Is hackish. But the structure is stable and
%% kept this way for backward compatibility reasons. We need
%% it for two reasons: there are no ssl:getstat(Sock) function,
%% and no ssl:close(Timeout) function. Both of them are being
%% worked on as we speak.
ssl_get_socket(Sock) ->
element(2, element(2, Sock)).
ssl_info(Sock) when ?IS_SSL(Sock) -> ssl_info(Sock) when ?IS_SSL(Sock) ->
ssl:connection_information(Sock); ssl:connection_information(Sock);
ssl_info(_Sock) -> ssl_info(_Sock) ->
@ -119,12 +110,12 @@ controlling_process(Sock, Pid) when is_port(Sock) ->
gen_tcp:controlling_process(Sock, Pid). gen_tcp:controlling_process(Sock, Pid).
getstat(Sock, Stats) when ?IS_SSL(Sock) -> getstat(Sock, Stats) when ?IS_SSL(Sock) ->
inet:getstat(ssl_get_socket(Sock), Stats); ssl:getstat(Sock, Stats);
getstat(Sock, Stats) when is_port(Sock) -> getstat(Sock, Stats) when is_port(Sock) ->
inet:getstat(Sock, Stats); inet:getstat(Sock, Stats);
%% Used by Proxy protocol support in plugins %% Used by Proxy protocol support in plugins
getstat({rabbit_proxy_socket, Sock, _}, Stats) when ?IS_SSL(Sock) -> getstat({rabbit_proxy_socket, Sock, _}, Stats) when ?IS_SSL(Sock) ->
inet:getstat(ssl_get_socket(Sock), Stats); ssl:getstat(Sock, Stats);
getstat({rabbit_proxy_socket, Sock, _}, Stats) when is_port(Sock) -> getstat({rabbit_proxy_socket, Sock, _}, Stats) when is_port(Sock) ->
inet:getstat(Sock, Stats). inet:getstat(Sock, Stats).
@ -177,27 +168,7 @@ close(Sock) when ?IS_SSL(Sock) -> ssl:close(Sock);
close(Sock) when is_port(Sock) -> gen_tcp:close(Sock). close(Sock) when is_port(Sock) -> gen_tcp:close(Sock).
fast_close(Sock) when ?IS_SSL(Sock) -> fast_close(Sock) when ?IS_SSL(Sock) ->
%% We cannot simply port_close the underlying tcp socket since the _ = ssl:close(Sock, ?SSL_CLOSE_TIMEOUT),
%% TLS protocol is quite insistent that a proper closing handshake
%% should take place (see RFC 5245 s7.2.1). So we call ssl:close
%% instead, but that can block for a very long time, e.g. when
%% there is lots of pending output and there is tcp backpressure,
%% or the ssl_connection process has entered the the
%% workaround_transport_delivery_problems function during
%% termination, which, inexplicably, does a gen_tcp:recv(Socket,
%% 0), which may never return if the client doesn't send a FIN or
%% that gets swallowed by the network. Since there is no timeout
%% variant of ssl:close, we construct our own.
{Pid, MRef} = spawn_monitor(fun () -> ssl:close(Sock) end),
erlang:send_after(?SSL_CLOSE_TIMEOUT, self(), {Pid, ssl_close_timeout}),
receive
{Pid, ssl_close_timeout} ->
erlang:demonitor(MRef, [flush]),
exit(Pid, kill);
{'DOWN', MRef, process, Pid, _Reason} ->
ok
end,
catch port_close(ssl_get_socket(Sock)),
ok; ok;
fast_close(Sock) when is_port(Sock) -> fast_close(Sock) when is_port(Sock) ->
catch port_close(Sock), ok. catch port_close(Sock), ok.

View File

@ -23,7 +23,9 @@ parse_endpoint(Destination, AllowAnonymousQueue)
parse_endpoint(Destination, AllowAnonymousQueue) parse_endpoint(Destination, AllowAnonymousQueue)
when is_list(Destination) -> when is_list(Destination) ->
case re:split(Destination, "/", [unicode, {return, list}]) of case re:split(Destination, "/", [unicode, {return, list}]) of
[Name] -> [] -> %% in OTP28+, re:split("", "/") returns []
{ok, {queue, unescape("")}};
[Name] -> %% before OTP28, re:split("", "/") returns [[]]
{ok, {queue, unescape(Name)}}; {ok, {queue, unescape(Name)}};
["", Type | Rest] ["", Type | Rest]
when Type =:= "exchange" orelse Type =:= "queue" orelse when Type =:= "exchange" orelse Type =:= "queue" orelse

View File

@ -21,8 +21,8 @@ TEST_DEPS = amqp amqp_client temp x509 rabbit
dep_amqp = hex 3.3.0 dep_amqp = hex 3.3.0
dep_csv = hex 3.2.1 dep_csv = hex 3.2.1
dep_json = hex 1.4.1 dep_json = hex 1.4.1
dep_temp = hex 0.4.7 dep_temp = hex 0.4.9
dep_x509 = hex 0.8.8 dep_x509 = hex 0.9.0
DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
DEP_PLUGINS = rabbit_common/mk/rabbitmq-build.mk DEP_PLUGINS = rabbit_common/mk/rabbitmq-build.mk

View File

@ -99,7 +99,7 @@ defmodule SetPermissionsCommandTest do
assert @command.run( assert @command.run(
[context[:user], "^#{context[:user]}-.*", ".*", "*"], [context[:user], "^#{context[:user]}-.*", ".*", "*"],
context[:opts] context[:opts]
) == {:error, {:invalid_regexp, ~c"*", {~c"nothing to repeat", 0}}} ) == {:error, {:invalid_regexp, ~c"*", {~c"quantifier does not follow a repeatable item", 0}}}
# asserts that the failed command didn't change anything # asserts that the failed command didn't change anything
u = Enum.find(list_permissions(context[:vhost]), fn x -> x[:user] == context[:user] end) u = Enum.find(list_permissions(context[:vhost]), fn x -> x[:user] == context[:user] end)

View File

@ -100,7 +100,7 @@ defmodule SetPermissionsGloballyCommandTest do
assert @command.run( assert @command.run(
[context[:user], "^#{context[:user]}-.*", ".*", "*"], [context[:user], "^#{context[:user]}-.*", ".*", "*"],
context[:opts] context[:opts]
) == {:error, {:invalid_regexp, ~c"*", {~c"nothing to repeat", 0}}} ) == {:error, {:invalid_regexp, ~c"*", {~c"quantifier does not follow a repeatable item", 0}}}
# asserts that the failed command didn't change anything # asserts that the failed command didn't change anything
p4 = Enum.find(list_permissions(@vhost1), fn x -> x[:user] == context[:user] end) p4 = Enum.find(list_permissions(@vhost1), fn x -> x[:user] == context[:user] end)

View File

@ -68,6 +68,24 @@ defmodule DisablePluginsCommandTest do
} }
end end
# Helper functions for order-insensitive assertions
defp normalize_result_map(map) when is_map(map) do
map
|> Map.update(:stopped, [], &Enum.sort/1)
|> Map.update(:disabled, [], &Enum.sort/1)
|> Map.update(:set, [], &Enum.sort/1)
end
defp normalize_stream_result([list, map]) when is_list(list) and is_map(map) do
[Enum.sort(list), normalize_result_map(map)]
end
defp normalize_stream_result(other), do: other
defp assert_lists_equal(expected, actual) do
assert Enum.sort(expected) == Enum.sort(actual)
end
test "validate: specifying both --online and --offline is reported as invalid", context do test "validate: specifying both --online and --offline is reported as invalid", context do
assert match?( assert match?(
{:validation_failure, {:bad_argument, _}}, {:validation_failure, {:bad_argument, _}},
@ -104,16 +122,18 @@ defmodule DisablePluginsCommandTest do
assert {:stream, test_stream} = assert {:stream, test_stream} =
@command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{node: :nonode})) @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{node: :nonode}))
assert [ result = Enum.to_list(test_stream)
[:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation], expected = [
%{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation]} [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation],
] == %{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation]}
Enum.to_list(test_stream) ]
assert normalize_stream_result(expected) == normalize_stream_result(result)
assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file]) assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file])
assert [:amqp_client, :rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_stomp] == result = :rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])
Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) expected = [:amqp_client, :rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_stomp]
assert_lists_equal(expected, result)
end end
test "in offline mode, writes out enabled plugins and reports implicitly enabled plugin list", test "in offline mode, writes out enabled plugins and reports implicitly enabled plugin list",
@ -124,15 +144,18 @@ defmodule DisablePluginsCommandTest do
Map.merge(context[:opts], %{offline: true, online: false}) Map.merge(context[:opts], %{offline: true, online: false})
) )
assert [ result = Enum.to_list(test_stream)
[:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation], expected = [
%{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation]} [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation],
] == Enum.to_list(test_stream) %{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation]}
]
assert normalize_stream_result(expected) == normalize_stream_result(result)
assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file]) assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file])
assert [:amqp_client, :rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_stomp] == active_plugins = :rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])
Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) expected_active = [:amqp_client, :rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_stomp]
assert_lists_equal(expected_active, active_plugins)
end end
test "in offline mode, removes implicitly enabled plugins when the last explicitly enabled one is removed", test "in offline mode, removes implicitly enabled plugins when the last explicitly enabled one is removed",
@ -143,10 +166,12 @@ defmodule DisablePluginsCommandTest do
Map.merge(context[:opts], %{offline: true, online: false}) Map.merge(context[:opts], %{offline: true, online: false})
) )
assert [ result = Enum.to_list(test_stream0)
[:rabbitmq_stomp], expected = [
%{mode: :offline, disabled: [:rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_exchange_federation, :rabbitmq_federation], set: [:rabbitmq_stomp]} [:rabbitmq_stomp],
] == Enum.to_list(test_stream0) %{mode: :offline, disabled: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation], set: [:rabbitmq_stomp]}
]
assert normalize_stream_result(expected) == normalize_stream_result(result)
assert {:ok, [[:rabbitmq_stomp]]} == :file.consult(context[:opts][:enabled_plugins_file]) assert {:ok, [[:rabbitmq_stomp]]} == :file.consult(context[:opts][:enabled_plugins_file])
@ -156,8 +181,9 @@ defmodule DisablePluginsCommandTest do
Map.merge(context[:opts], %{offline: true, online: false}) Map.merge(context[:opts], %{offline: true, online: false})
) )
assert [[], %{mode: :offline, disabled: [:rabbitmq_stomp], set: []}] == result = Enum.to_list(test_stream1)
Enum.to_list(test_stream1) expected = [[], %{mode: :offline, disabled: [:rabbitmq_stomp], set: []}]
assert normalize_stream_result(expected) == normalize_stream_result(result)
assert {:ok, [[]]} = :file.consult(context[:opts][:enabled_plugins_file]) assert {:ok, [[]]} = :file.consult(context[:opts][:enabled_plugins_file])
end end
@ -165,102 +191,90 @@ defmodule DisablePluginsCommandTest do
test "updates plugin list and stops disabled plugins", context do test "updates plugin list and stops disabled plugins", context do
assert {:stream, test_stream0} = @command.run(["rabbitmq_stomp"], context[:opts]) assert {:stream, test_stream0} = @command.run(["rabbitmq_stomp"], context[:opts])
assert [ result = Enum.to_list(test_stream0)
[:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation], expected = [
%{ [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation],
mode: :online, %{
started: [], mode: :online,
stopped: [:rabbitmq_stomp], started: [],
disabled: [:rabbitmq_stomp], stopped: [:rabbitmq_stomp],
set: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation] disabled: [:rabbitmq_stomp],
} set: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation]
] == }
Enum.to_list(test_stream0) ]
assert normalize_stream_result(expected) == normalize_stream_result(result)
assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file]) assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file])
assert [:amqp_client, :rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation] == result = :rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])
Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) expected = [:amqp_client, :rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation]
assert_lists_equal(expected, result)
assert {:stream, test_stream1} = @command.run(["rabbitmq_federation"], context[:opts]) assert {:stream, test_stream1} = @command.run(["rabbitmq_federation"], context[:opts])
assert [ result = Enum.to_list(test_stream1)
[], expected = [
%{ [],
mode: :online, %{
started: [], mode: :online,
stopped: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation], started: [],
disabled: [:rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_exchange_federation, :rabbitmq_federation], stopped: [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation],
set: [] disabled: [:rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_exchange_federation, :rabbitmq_federation],
} set: []
] == }
Enum.to_list(test_stream1) ]
assert normalize_stream_result(expected) == normalize_stream_result(result)
assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file]) assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file])
assert Enum.empty?( result = :rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])
Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) assert Enum.empty?(result)
)
end end
test "can disable multiple plugins at once", context do test "can disable multiple plugins at once", context do
assert {:stream, test_stream} = assert {:stream, test_stream} =
@command.run(["rabbitmq_stomp", "rabbitmq_federation"], context[:opts]) @command.run(["rabbitmq_stomp", "rabbitmq_federation"], context[:opts])
[[], m0] = Enum.to_list(test_stream) result = Enum.to_list(test_stream)
expected_list = [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_stomp]
m1 = expected = [
m0 [],
|> Map.update!(:stopped, &Enum.sort/1) %{
|> Map.update!(:disabled, &Enum.sort/1) mode: :online,
started: [],
expected_list = Enum.sort([:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_stomp]) stopped: expected_list,
disabled: expected_list,
assert [ set: []
[], }
%{ ]
mode: :online, assert normalize_stream_result(expected) == normalize_stream_result(result)
started: [],
stopped: expected_list,
disabled: expected_list,
set: []
}
] == [[], m1]
assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file]) assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file])
assert Enum.empty?( active_plugins = :rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])
Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) assert Enum.empty?(active_plugins)
)
end end
test "disabling a dependency disables all plugins that depend on it", context do test "disabling a dependency disables all plugins that depend on it", context do
assert {:stream, test_stream} = @command.run(["amqp_client"], context[:opts]) assert {:stream, test_stream} = @command.run(["amqp_client"], context[:opts])
[[], m0] = Enum.to_list(test_stream) result = Enum.to_list(test_stream)
expected_list = [:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_stomp]
m1 = expected = [
m0 [],
|> Map.update!(:stopped, &Enum.sort/1) %{
|> Map.update!(:disabled, &Enum.sort/1) mode: :online,
started: [],
expected_list = Enum.sort([:rabbitmq_exchange_federation, :rabbitmq_federation, :rabbitmq_federation_common, :rabbitmq_queue_federation, :rabbitmq_stomp]) stopped: expected_list,
disabled: expected_list,
assert [ set: []
[], }
%{ ]
mode: :online, assert normalize_stream_result(expected) == normalize_stream_result(result)
started: [],
stopped: expected_list,
disabled: expected_list,
set: []
}
] == [[], m1]
assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file]) assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file])
assert Enum.empty?( result = :rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])
Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) assert Enum.empty?(result)
)
end end
test "formats enabled plugins mismatch errors", context do test "formats enabled plugins mismatch errors", context do

View File

@ -5,7 +5,7 @@ DEPS = rabbit_common rabbitmq_ct_helpers amqp_client
DEP_PLUGINS = rabbit_common/mk/rabbitmq-build.mk DEP_PLUGINS = rabbit_common/mk/rabbitmq-build.mk
PLT_APPS += common_test crypto PLT_APPS += common_test crypto ssl
include ../../rabbitmq-components.mk include ../../rabbitmq-components.mk
include ../../erlang.mk include ../../erlang.mk

View File

@ -23,7 +23,7 @@ new(WsUrl, PPid, AuthInfo, Protocols) ->
new(WsUrl, PPid, AuthInfo, Protocols, <<>>). new(WsUrl, PPid, AuthInfo, Protocols, <<>>).
new(WsUrl, PPid, AuthInfo, Protocols, TcpPreface) -> new(WsUrl, PPid, AuthInfo, Protocols, TcpPreface) ->
_ = crypto:start(), _ = application:start(crypto),
_ = application:ensure_all_started(ssl), _ = application:ensure_all_started(ssl),
{Transport, Url} = case WsUrl of {Transport, Url} = case WsUrl of
"ws://" ++ Rest -> {gen_tcp, Rest}; "ws://" ++ Rest -> {gen_tcp, Rest};

View File

@ -73,6 +73,7 @@
-compile(inline). -compile(inline).
-compile(inline_list_funcs). -compile(inline_list_funcs).
-compile({no_auto_import,[ceil/1]}).
-type value() :: tuple(). -type value() :: tuple().
-type internal_value() :: tuple() | drop. -type internal_value() :: tuple() | drop.

View File

@ -33,7 +33,7 @@ endef
DEPS = ranch rabbit_common rabbit amqp_client DEPS = ranch rabbit_common rabbit amqp_client
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers rabbitmq_management TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers rabbitmq_management
PLT_APPS += rabbitmq_cli elixir PLT_APPS += rabbitmq_cli elixir ssl
DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk

View File

@ -25,7 +25,7 @@ LOCAL_DEPS = ssl
DEPS = rabbit rabbitmq_stream_common osiris ranch DEPS = rabbit rabbitmq_stream_common osiris ranch
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client amqp10_client TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client amqp10_client
PLT_APPS += rabbitmq_cli elixir PLT_APPS += rabbitmq_cli elixir ssl
DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk

View File

@ -21,7 +21,7 @@ LOCAL_DEPS = ssl
DEPS = rabbit cowboy rabbitmq_mqtt DEPS = rabbit cowboy rabbitmq_mqtt
TEST_DEPS = emqtt rabbitmq_ct_helpers rabbitmq_ct_client_helpers rabbitmq_management rabbitmq_stomp rabbitmq_consistent_hash_exchange TEST_DEPS = emqtt rabbitmq_ct_helpers rabbitmq_ct_client_helpers rabbitmq_management rabbitmq_stomp rabbitmq_consistent_hash_exchange
PLT_APPS += rabbitmq_cli elixir cowlib PLT_APPS += rabbitmq_cli elixir cowlib ssl
# FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked. # FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked.
# See rabbitmq-components.mk. # See rabbitmq-components.mk.

17
erlang.mk vendored
View File

@ -17,7 +17,7 @@
ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST))) ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
export ERLANG_MK_FILENAME export ERLANG_MK_FILENAME
ERLANG_MK_VERSION = e13b4c7 ERLANG_MK_VERSION = f157f11
ERLANG_MK_WITHOUT = ERLANG_MK_WITHOUT =
# Make 3.81 and 3.82 are deprecated. # Make 3.81 and 3.82 are deprecated.
@ -559,6 +559,14 @@ export ERL_LIBS
export NO_AUTOPATCH export NO_AUTOPATCH
# Elixir.
# Elixir is automatically enabled in all cases except when
# an Erlang project uses an Elixir dependency. In that case
# $(ELIXIR) must be set explicitly.
ELIXIR ?= $(if $(filter elixir,$(BUILD_DEPS) $(DEPS)),dep,$(if $(EX_FILES),system,disable))
export ELIXIR
# Verbosity. # Verbosity.
dep_verbose_0 = @echo " DEP $1 ($(call query_version,$1))"; dep_verbose_0 = @echo " DEP $1 ($(call query_version,$1))";
@ -1778,12 +1786,6 @@ endif
# Copyright (c) 2024, Loïc Hoguin <essen@ninenines.eu> # Copyright (c) 2024, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License. # This file is part of erlang.mk and subject to the terms of the ISC License.
# Elixir is automatically enabled in all cases except when
# an Erlang project uses an Elixir dependency. In that case
# $(ELIXIR) must be set explicitly.
ELIXIR ?= $(if $(filter elixir,$(BUILD_DEPS) $(DEPS)),dep,$(if $(EX_FILES),system,disable))
export ELIXIR
ifeq ($(ELIXIR),system) ifeq ($(ELIXIR),system)
# We expect 'elixir' to be on the path. # We expect 'elixir' to be on the path.
ELIXIR_BIN ?= $(shell readlink -f `which elixir`) ELIXIR_BIN ?= $(shell readlink -f `which elixir`)
@ -1964,6 +1966,7 @@ endef
define compile_ex.erl define compile_ex.erl
{ok, _} = application:ensure_all_started(elixir), {ok, _} = application:ensure_all_started(elixir),
{ok, _} = application:ensure_all_started(mix), {ok, _} = application:ensure_all_started(mix),
$(foreach dep,$(LOCAL_DEPS),_ = application:load($(dep)),)
ModCode = list_to_atom("Elixir.Code"), ModCode = list_to_atom("Elixir.Code"),
ModCode:put_compiler_option(ignore_module_conflict, true), ModCode:put_compiler_option(ignore_module_conflict, true),
ModComp = list_to_atom("Elixir.Kernel.ParallelCompiler"), ModComp = list_to_atom("Elixir.Kernel.ParallelCompiler"),