diff --git a/.github/workflows/test-make-tests.yaml b/.github/workflows/test-make-tests.yaml index a4ffd93c453c..cdffd87189d7 100644 --- a/.github/workflows/test-make-tests.yaml +++ b/.github/workflows/test-make-tests.yaml @@ -86,7 +86,8 @@ jobs: - rabbitmq_cli - rabbitmq_consistent_hash_exchange - rabbitmq_event_exchange - - rabbitmq_federation + - rabbitmq_exchange_federation + - rabbitmq_federation_common - rabbitmq_federation_management - rabbitmq_federation_prometheus - rabbitmq_jms_topic_exchange @@ -98,6 +99,7 @@ jobs: - rabbitmq_peer_discovery_k8s - rabbitmq_prelaunch - rabbitmq_prometheus + - rabbitmq_queue_federation - rabbitmq_recent_history_exchange - rabbitmq_sharding - rabbitmq_shovel diff --git a/.github/workflows/test-make-type-check.yaml b/.github/workflows/test-make-type-check.yaml index bf977874aff9..d1459bceeb26 100644 --- a/.github/workflows/test-make-type-check.yaml +++ b/.github/workflows/test-make-type-check.yaml @@ -34,7 +34,8 @@ jobs: - rabbitmq_aws - rabbitmq_consistent_hash_exchange - rabbitmq_event_exchange - - rabbitmq_federation + - rabbitmq_exchange_federation + - rabbitmq_federation_common - rabbitmq_federation_management - rabbitmq_federation_prometheus - rabbitmq_jms_topic_exchange @@ -50,6 +51,7 @@ jobs: - rabbitmq_peer_discovery_k8s - rabbitmq_prelaunch - rabbitmq_prometheus + - rabbitmq_queue_federation - rabbitmq_recent_history_exchange - rabbitmq_sharding - rabbitmq_shovel diff --git a/.gitignore b/.gitignore index eee87485f4e8..272050aff697 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,9 @@ elvis !/deps/rabbitmq_ct_helpers/ !/deps/rabbitmq_ct_client_helpers/ !/deps/rabbitmq_event_exchange/ +!/deps/rabbitmq_exchange_federation/ !/deps/rabbitmq_federation/ +!/deps/rabbitmq_federation_common/ !/deps/rabbitmq_federation_management/ !/deps/rabbitmq_federation_prometheus/ !/deps/rabbitmq_jms_topic_exchange/ @@ -62,6 +64,7 @@ elvis !/deps/rabbitmq_peer_discovery_k8s/ !/deps/rabbitmq_prelaunch/ !/deps/rabbitmq_prometheus/ +!/deps/rabbitmq_queue_federation/ !/deps/rabbitmq_random_exchange/ !/deps/rabbitmq_recent_history_exchange/ !/deps/rabbitmq_sharding/ diff --git a/deps/rabbitmq_cli/Makefile b/deps/rabbitmq_cli/Makefile index ac74acc6880d..ee55b9a13007 100644 --- a/deps/rabbitmq_cli/Makefile +++ b/deps/rabbitmq_cli/Makefile @@ -122,7 +122,7 @@ $(LINKED_ESCRIPTS): $(ESCRIPT_FILE) tests:: escript test-deps $(verbose) $(MAKE) -C ../../ install-cli $(verbose) $(MAKE) -C ../../ start-background-broker \ - PLUGINS="rabbitmq_federation rabbitmq_stomp rabbitmq_stream_management amqp_client" \ + PLUGINS="rabbitmq_federation_common rabbitmq_stomp rabbitmq_stream_management amqp_client" \ $(if $(filter khepri,$(RABBITMQ_METADATA_STORE)),,RABBITMQ_FEATURE_FLAGS="-khepri_db") $(gen_verbose) $(MIX_TEST) \ $(if $(RABBITMQ_METADATA_STORE),--exclude $(filter-out $(RABBITMQ_METADATA_STORE),khepri mnesia),) \ diff --git a/deps/rabbitmq_cli/test/ctl/list_parameters_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_parameters_command_test.exs index 022b41e814be..eb21bc055fce 100644 --- a/deps/rabbitmq_cli/test/ctl/list_parameters_command_test.exs +++ b/deps/rabbitmq_cli/test/ctl/list_parameters_command_test.exs @@ -37,7 +37,7 @@ defmodule ListParametersCommandTest do rabbitmq_home: rabbitmq_home } - set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation], :online, node, opts) + set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation_common], :online, node, opts) add_vhost(@vhost) diff --git a/deps/rabbitmq_cli/test/plugins/disable_plugins_command_test.exs b/deps/rabbitmq_cli/test/plugins/disable_plugins_command_test.exs index f8b5ef5a644b..19938f83dda1 100644 --- a/deps/rabbitmq_cli/test/plugins/disable_plugins_command_test.exs +++ b/deps/rabbitmq_cli/test/plugins/disable_plugins_command_test.exs @@ -52,7 +52,7 @@ defmodule DisablePluginsCommandTest do setup context do set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, get_rabbit_hostname(), context[:opts] @@ -105,14 +105,14 @@ defmodule DisablePluginsCommandTest do @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{node: :nonode})) assert [ - [:rabbitmq_federation], - %{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_federation]} + [:rabbitmq_federation_common], + %{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_federation_common]} ] == Enum.to_list(test_stream) - assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file]) + assert {:ok, [[:rabbitmq_federation_common]]} == :file.consult(context[:opts][:enabled_plugins_file]) - assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] == + assert [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp] == Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) end @@ -125,13 +125,13 @@ defmodule DisablePluginsCommandTest do ) assert [ - [:rabbitmq_federation], - %{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_federation]} + [:rabbitmq_federation_common], + %{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_federation_common]} ] == Enum.to_list(test_stream) - assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file]) + assert {:ok, [[:rabbitmq_federation_common]]} == :file.consult(context[:opts][:enabled_plugins_file]) - assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] == + assert [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp] == Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) end @@ -139,13 +139,13 @@ defmodule DisablePluginsCommandTest do context do assert {:stream, test_stream0} = @command.run( - ["rabbitmq_federation"], + ["rabbitmq_federation_common"], Map.merge(context[:opts], %{offline: true, online: false}) ) assert [ [:rabbitmq_stomp], - %{mode: :offline, disabled: [:rabbitmq_federation], set: [:rabbitmq_stomp]} + %{mode: :offline, disabled: [:rabbitmq_federation_common], set: [:rabbitmq_stomp]} ] == Enum.to_list(test_stream0) assert {:ok, [[:rabbitmq_stomp]]} == :file.consult(context[:opts][:enabled_plugins_file]) @@ -166,31 +166,31 @@ defmodule DisablePluginsCommandTest do assert {:stream, test_stream0} = @command.run(["rabbitmq_stomp"], context[:opts]) assert [ - [:rabbitmq_federation], + [:rabbitmq_federation_common], %{ mode: :online, started: [], stopped: [:rabbitmq_stomp], disabled: [:rabbitmq_stomp], - set: [:rabbitmq_federation] + set: [:rabbitmq_federation_common] } ] == Enum.to_list(test_stream0) - assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file]) + assert {:ok, [[:rabbitmq_federation_common]]} == :file.consult(context[:opts][:enabled_plugins_file]) - assert [:amqp_client, :rabbitmq_federation] == + assert [:amqp_client, :rabbitmq_federation_common] == Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) - assert {:stream, test_stream1} = @command.run(["rabbitmq_federation"], context[:opts]) + assert {:stream, test_stream1} = @command.run(["rabbitmq_federation_common"], context[:opts]) assert [ [], %{ mode: :online, started: [], - stopped: [:rabbitmq_federation], - disabled: [:rabbitmq_federation], + stopped: [:rabbitmq_federation_common], + disabled: [:rabbitmq_federation_common], set: [] } ] == @@ -205,7 +205,7 @@ defmodule DisablePluginsCommandTest do test "can disable multiple plugins at once", context do assert {:stream, test_stream} = - @command.run(["rabbitmq_stomp", "rabbitmq_federation"], context[:opts]) + @command.run(["rabbitmq_stomp", "rabbitmq_federation_common"], context[:opts]) [[], m0] = Enum.to_list(test_stream) @@ -214,7 +214,7 @@ defmodule DisablePluginsCommandTest do |> Map.update!(:stopped, &Enum.sort/1) |> Map.update!(:disabled, &Enum.sort/1) - expected_list = Enum.sort([:rabbitmq_federation, :rabbitmq_stomp]) + expected_list = Enum.sort([:rabbitmq_federation_common, :rabbitmq_stomp]) assert [ [], @@ -243,7 +243,7 @@ defmodule DisablePluginsCommandTest do |> Map.update!(:stopped, &Enum.sort/1) |> Map.update!(:disabled, &Enum.sort/1) - expected_list = Enum.sort([:rabbitmq_federation, :rabbitmq_stomp]) + expected_list = Enum.sort([:rabbitmq_federation_common, :rabbitmq_stomp]) assert [ [], diff --git a/deps/rabbitmq_cli/test/plugins/enable_plugins_command_test.exs b/deps/rabbitmq_cli/test/plugins/enable_plugins_command_test.exs index 424a9ade1aad..6740cd89e889 100644 --- a/deps/rabbitmq_cli/test/plugins/enable_plugins_command_test.exs +++ b/deps/rabbitmq_cli/test/plugins/enable_plugins_command_test.exs @@ -66,7 +66,7 @@ defmodule EnablePluginsCommandTest do def reset_enabled_plugins_to_preconfigured_defaults(context) do set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, get_rabbit_hostname(), context[:opts] @@ -120,7 +120,7 @@ defmodule EnablePluginsCommandTest do check_plugins_enabled([:rabbitmq_stomp], context) - assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] == + assert [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp] == currently_active_plugins(context) end @@ -144,7 +144,7 @@ defmodule EnablePluginsCommandTest do check_plugins_enabled([:rabbitmq_stomp], context) assert_equal_sets( - [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp], + [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp], currently_active_plugins(context) ) end @@ -169,21 +169,21 @@ defmodule EnablePluginsCommandTest do assert {:stream, test_stream1} = @command.run( - ["rabbitmq_federation"], + ["rabbitmq_federation_common"], Map.merge(context[:opts], %{offline: true, online: false}) ) assert [ - [:rabbitmq_federation, :rabbitmq_stomp], + [:rabbitmq_federation_common, :rabbitmq_stomp], %{ mode: :offline, - enabled: [:rabbitmq_federation], - set: [:rabbitmq_federation, :rabbitmq_stomp] + enabled: [:rabbitmq_federation_common], + set: [:rabbitmq_federation_common, :rabbitmq_stomp] } ] == Enum.to_list(test_stream1) - check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation], context) + check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation_common], context) end test "run: updates plugin list and starts newly enabled plugins", context do @@ -207,24 +207,24 @@ defmodule EnablePluginsCommandTest do check_plugins_enabled([:rabbitmq_stomp], context) assert_equal_sets([:amqp_client, :rabbitmq_stomp], currently_active_plugins(context)) - {:stream, test_stream1} = @command.run(["rabbitmq_federation"], context[:opts]) + {:stream, test_stream1} = @command.run(["rabbitmq_federation_common"], context[:opts]) assert [ - [:rabbitmq_federation, :rabbitmq_stomp], + [:rabbitmq_federation_common, :rabbitmq_stomp], %{ mode: :online, - started: [:rabbitmq_federation], + started: [:rabbitmq_federation_common], stopped: [], - enabled: [:rabbitmq_federation], - set: [:rabbitmq_federation, :rabbitmq_stomp] + enabled: [:rabbitmq_federation_common], + set: [:rabbitmq_federation_common, :rabbitmq_stomp] } ] == Enum.to_list(test_stream1) - check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation], context) + check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation_common], context) assert_equal_sets( - [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp], + [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp], currently_active_plugins(context) ) @@ -236,24 +236,24 @@ defmodule EnablePluginsCommandTest do set_enabled_plugins([], :online, context[:opts][:node], context[:opts]) assert {:stream, test_stream} = - @command.run(["rabbitmq_stomp", "rabbitmq_federation"], context[:opts]) + @command.run(["rabbitmq_stomp", "rabbitmq_federation_common"], context[:opts]) assert [ - [:rabbitmq_federation, :rabbitmq_stomp], + [:rabbitmq_federation_common, :rabbitmq_stomp], %{ mode: :online, - started: [:rabbitmq_federation, :rabbitmq_stomp], + started: [:rabbitmq_federation_common, :rabbitmq_stomp], stopped: [], - enabled: [:rabbitmq_federation, :rabbitmq_stomp], - set: [:rabbitmq_federation, :rabbitmq_stomp] + enabled: [:rabbitmq_federation_common, :rabbitmq_stomp], + set: [:rabbitmq_federation_common, :rabbitmq_stomp] } ] == Enum.to_list(test_stream) - check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation], context) + check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation_common], context) assert_equal_sets( - [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp], + [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp], currently_active_plugins(context) ) @@ -262,18 +262,18 @@ defmodule EnablePluginsCommandTest do test "run: does not enable an already implicitly enabled plugin", context do # Clears enabled plugins file and stop all plugins - set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts]) + set_enabled_plugins([:rabbitmq_federation_common], :online, context[:opts][:node], context[:opts]) assert {:stream, test_stream} = @command.run(["amqp_client"], context[:opts]) assert [ - [:rabbitmq_federation], - %{mode: :online, started: [], stopped: [], enabled: [], set: [:rabbitmq_federation]} + [:rabbitmq_federation_common], + %{mode: :online, started: [], stopped: [], enabled: [], set: [:rabbitmq_federation_common]} ] == Enum.to_list(test_stream) - check_plugins_enabled([:rabbitmq_federation], context) + check_plugins_enabled([:rabbitmq_federation_common], context) - assert [:amqp_client, :rabbitmq_federation] == + assert [:amqp_client, :rabbitmq_federation_common] == currently_active_plugins(context) reset_enabled_plugins_to_preconfigured_defaults(context) diff --git a/deps/rabbitmq_cli/test/plugins/is_enabled_command_test.exs b/deps/rabbitmq_cli/test/plugins/is_enabled_command_test.exs index 3fdc83350735..39d9b4d5c8df 100644 --- a/deps/rabbitmq_cli/test/plugins/is_enabled_command_test.exs +++ b/deps/rabbitmq_cli/test/plugins/is_enabled_command_test.exs @@ -99,7 +99,7 @@ defmodule PluginIsEnabledCommandTest do assert match?( {:ok, _}, - assert(@command.run(["rabbitmq_stomp", "rabbitmq_federation"], opts)) + assert(@command.run(["rabbitmq_stomp", "rabbitmq_federation_common"], opts)) ) end diff --git a/deps/rabbitmq_cli/test/plugins/list_plugins_command_test.exs b/deps/rabbitmq_cli/test/plugins/list_plugins_command_test.exs index 4bd6fb764c80..8ec2936804a2 100644 --- a/deps/rabbitmq_cli/test/plugins/list_plugins_command_test.exs +++ b/deps/rabbitmq_cli/test/plugins/list_plugins_command_test.exs @@ -13,7 +13,7 @@ defmodule ListPluginsCommandTest do def reset_enabled_plugins_to_preconfigured_defaults(context) do set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, get_rabbit_hostname(), context[:opts] @@ -124,7 +124,7 @@ defmodule ListPluginsCommandTest do end) expected_plugins = [ - %{name: :rabbitmq_federation, enabled: :enabled, running: false}, + %{name: :rabbitmq_federation_common, enabled: :enabled, running: false}, %{name: :rabbitmq_stomp, enabled: :enabled, running: false} ] @@ -146,7 +146,7 @@ defmodule ListPluginsCommandTest do end) expected_plugins = [ - %{name: :rabbitmq_federation, enabled: :enabled, running: true}, + %{name: :rabbitmq_federation_common, enabled: :enabled, running: true}, %{name: :rabbitmq_stomp, enabled: :enabled, running: false} ] @@ -163,7 +163,7 @@ defmodule ListPluginsCommandTest do expected_plugins = [ %{ - name: :rabbitmq_federation, + name: :rabbitmq_federation_common, enabled: :enabled, running: true, dependencies: [:amqp_client] @@ -186,7 +186,7 @@ defmodule ListPluginsCommandTest do test "run: reports plugin names in minimal mode", context do reset_enabled_plugins_to_preconfigured_defaults(context) - expected_plugins = [%{name: :rabbitmq_federation}, %{name: :rabbitmq_stomp}] + expected_plugins = [%{name: :rabbitmq_federation_common}, %{name: :rabbitmq_stomp}] %{status: :running, plugins: actual_plugins} = @command.run([".*"], Map.merge(context[:opts], %{minimal: true})) @@ -196,11 +196,11 @@ defmodule ListPluginsCommandTest do test "run: by default lists all plugins", context do reset_enabled_plugins_to_preconfigured_defaults(context) - set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts]) + set_enabled_plugins([:rabbitmq_federation_common], :online, context[:opts][:node], context[:opts]) on_exit(fn -> set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, context[:opts][:node], context[:opts] @@ -208,7 +208,7 @@ defmodule ListPluginsCommandTest do end) expected_plugins = [ - %{name: :rabbitmq_federation, enabled: :enabled, running: true}, + %{name: :rabbitmq_federation_common, enabled: :enabled, running: true}, %{name: :rabbitmq_stomp, enabled: :not_enabled, running: false} ] @@ -221,18 +221,18 @@ defmodule ListPluginsCommandTest do test "run: with --enabled flag, lists only explicitly enabled plugins", context do reset_enabled_plugins_to_preconfigured_defaults(context) - set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts]) + set_enabled_plugins([:rabbitmq_federation_common], :online, context[:opts][:node], context[:opts]) on_exit(fn -> set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, context[:opts][:node], context[:opts] ) end) - expected_plugins = [%{name: :rabbitmq_federation, enabled: :enabled, running: true}] + expected_plugins = [%{name: :rabbitmq_federation_common, enabled: :enabled, running: true}] %{ status: :running, @@ -245,18 +245,18 @@ defmodule ListPluginsCommandTest do test "run: with --implicitly_enabled flag lists explicitly and implicitly enabled plugins", context do reset_enabled_plugins_to_preconfigured_defaults(context) - set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts]) + set_enabled_plugins([:rabbitmq_federation_common], :online, context[:opts][:node], context[:opts]) on_exit(fn -> set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, context[:opts][:node], context[:opts] ) end) - expected_plugins = [%{name: :rabbitmq_federation, enabled: :enabled, running: true}] + expected_plugins = [%{name: :rabbitmq_federation_common, enabled: :enabled, running: true}] %{ status: :running, @@ -268,11 +268,11 @@ defmodule ListPluginsCommandTest do test "run: filters plugins by name with pattern provided", context do reset_enabled_plugins_to_preconfigured_defaults(context) - set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts]) + set_enabled_plugins([:rabbitmq_federation_common], :online, context[:opts][:node], context[:opts]) on_exit(fn -> set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, context[:opts][:node], context[:opts] @@ -282,7 +282,7 @@ defmodule ListPluginsCommandTest do %{status: :running, plugins: actual_plugins} = @command.run(["fede"], Map.merge(context[:opts], %{minimal: true})) - assert_plugin_states(actual_plugins, [%{name: :rabbitmq_federation}]) + assert_plugin_states(actual_plugins, [%{name: :rabbitmq_federation_common}]) %{status: :running, plugins: actual_plugins2} = @command.run(["stomp$"], Map.merge(context[:opts], %{minimal: true})) @@ -295,7 +295,7 @@ defmodule ListPluginsCommandTest do opts = get_opts_with_non_existing_plugins_directory(context) expected_plugins = [ - %{name: :rabbitmq_federation}, + %{name: :rabbitmq_federation_common}, %{name: :rabbitmq_stomp} ] @@ -310,7 +310,7 @@ defmodule ListPluginsCommandTest do reset_enabled_plugins_to_preconfigured_defaults(context) opts = get_opts_with_existing_plugins_directory(context) - expected_plugins = [%{name: :rabbitmq_federation}, %{name: :rabbitmq_stomp}] + expected_plugins = [%{name: :rabbitmq_federation_common}, %{name: :rabbitmq_stomp}] %{status: :running, plugins: actual_plugins} = @command.run([".*"], Map.merge(opts, %{minimal: true})) @@ -329,7 +329,7 @@ defmodule ListPluginsCommandTest do expected_plugins = [ %{name: :mock_rabbitmq_plugins_01}, %{name: :mock_rabbitmq_plugins_02}, - %{name: :rabbitmq_federation}, + %{name: :rabbitmq_federation_common}, %{name: :rabbitmq_stomp} ] @@ -365,7 +365,7 @@ defmodule ListPluginsCommandTest do running: false, version: ~c"0.2.0" }, - %{name: :rabbitmq_federation, enabled: :enabled, running: true}, + %{name: :rabbitmq_federation_common, enabled: :enabled, running: true}, %{name: :rabbitmq_stomp, enabled: :enabled, running: true} ] @@ -385,7 +385,7 @@ defmodule ListPluginsCommandTest do switch_plugins_directories(context[:opts][:plugins_dir], opts[:plugins_dir]) set_enabled_plugins( - [:mock_rabbitmq_plugins_02, :rabbitmq_federation, :rabbitmq_stomp], + [:mock_rabbitmq_plugins_02, :rabbitmq_federation_common, :rabbitmq_stomp], :online, get_rabbit_hostname(), opts @@ -405,7 +405,7 @@ defmodule ListPluginsCommandTest do version: ~c"0.1.0", running_version: ~c"0.1.0" }, - %{name: :rabbitmq_federation, enabled: :enabled, running: true}, + %{name: :rabbitmq_federation_common, enabled: :enabled, running: true}, %{name: :rabbitmq_stomp, enabled: :enabled, running: true} ] @@ -435,7 +435,7 @@ defmodule ListPluginsCommandTest do version: ~c"0.2.0", running_version: ~c"0.1.0" }, - %{name: :rabbitmq_federation, enabled: :enabled, running: true}, + %{name: :rabbitmq_federation_common, enabled: :enabled, running: true}, %{name: :rabbitmq_stomp, enabled: :enabled, running: true} ] @@ -459,7 +459,7 @@ defmodule ListPluginsCommandTest do reset_enabled_plugins_to_preconfigured_defaults(context) set_enabled_plugins( - [:rabbitmq_federation, missing_plugin], + [:rabbitmq_federation_common, missing_plugin], :online, context[:opts][:node], context[:opts] @@ -474,7 +474,7 @@ defmodule ListPluginsCommandTest do context = Map.replace(context, :opts, opts) set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, context[:opts][:node], context[:opts] @@ -482,7 +482,7 @@ defmodule ListPluginsCommandTest do end) expected_plugins = [ - %{name: :rabbitmq_federation, enabled: :enabled, running: true}, + %{name: :rabbitmq_federation_common, enabled: :enabled, running: true}, %{name: :rabbitmq_stomp, enabled: :not_enabled, running: false} ] @@ -520,7 +520,7 @@ defmodule ListPluginsCommandTest do reset_enabled_plugins_to_preconfigured_defaults(context) set_enabled_plugins( - [:rabbitmq_federation, missing_plugin], + [:rabbitmq_federation_common, missing_plugin], :online, context[:opts][:node], context[:opts] @@ -535,7 +535,7 @@ defmodule ListPluginsCommandTest do context = Map.replace(context, :opts, opts) set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, context[:opts][:node], context[:opts] @@ -543,7 +543,7 @@ defmodule ListPluginsCommandTest do end) expected_plugins = [ - %{name: :rabbitmq_federation, enabled: :enabled, running: true}, + %{name: :rabbitmq_federation_common, enabled: :enabled, running: true}, %{name: :rabbitmq_stomp, enabled: :not_enabled, running: false} ] diff --git a/deps/rabbitmq_cli/test/plugins/plugins_formatter_test.exs b/deps/rabbitmq_cli/test/plugins/plugins_formatter_test.exs index eda17b519905..4d6d476d93e9 100644 --- a/deps/rabbitmq_cli/test/plugins/plugins_formatter_test.exs +++ b/deps/rabbitmq_cli/test/plugins/plugins_formatter_test.exs @@ -37,7 +37,7 @@ defmodule PluginsFormatterTest do running_version: ~c"0.1.0" }, %{ - name: :rabbitmq_federation, + name: :rabbitmq_federation_common, enabled: :enabled, running: true, version: ~c"3.7.0", @@ -60,7 +60,7 @@ defmodule PluginsFormatterTest do "amqp_client", "mock_rabbitmq_plugins_01", "mock_rabbitmq_plugins_02", - "rabbitmq_federation", + "rabbitmq_federation_common", "rabbitmq_stomp" ] end @@ -93,7 +93,7 @@ defmodule PluginsFormatterTest do running_version: ~c"0.1.0" }, %{ - name: :rabbitmq_federation, + name: :rabbitmq_federation_common, enabled: :enabled, running: true, version: ~c"3.7.0", @@ -116,11 +116,11 @@ defmodule PluginsFormatterTest do " Configured: E = explicitly enabled; e = implicitly enabled", " | Status: * = running on rabbit@localhost", " |/", - "[e*] amqp_client 3.7.0", - "[ ] mock_rabbitmq_plugins_01 0.2.0", - "[E*] mock_rabbitmq_plugins_02 0.1.0 (pending upgrade to 0.2.0)", - "[E*] rabbitmq_federation 3.7.0", - "[E*] rabbitmq_stomp 3.7.0" + "[e*] amqp_client 3.7.0", + "[ ] mock_rabbitmq_plugins_01 0.2.0", + "[E*] mock_rabbitmq_plugins_02 0.1.0 (pending upgrade to 0.2.0)", + "[E*] rabbitmq_federation_common 3.7.0", + "[E*] rabbitmq_stomp 3.7.0" ] end end diff --git a/deps/rabbitmq_cli/test/plugins/set_plugins_command_test.exs b/deps/rabbitmq_cli/test/plugins/set_plugins_command_test.exs index e25af5c1f584..dea77614728a 100644 --- a/deps/rabbitmq_cli/test/plugins/set_plugins_command_test.exs +++ b/deps/rabbitmq_cli/test/plugins/set_plugins_command_test.exs @@ -41,7 +41,7 @@ defmodule SetPluginsCommandTest do setup context do set_enabled_plugins( - [:rabbitmq_stomp, :rabbitmq_federation], + [:rabbitmq_stomp, :rabbitmq_federation_common], :online, get_rabbit_hostname(), context[:opts] @@ -92,7 +92,7 @@ defmodule SetPluginsCommandTest do assert {:ok, [[:rabbitmq_stomp]]} = :file.consult(context[:opts][:enabled_plugins_file]) - assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] = + assert [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp] = Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) end @@ -108,7 +108,7 @@ defmodule SetPluginsCommandTest do assert {:ok, [[:rabbitmq_stomp]]} = :file.consult(context[:opts][:enabled_plugins_file]) - assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] = + assert [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp] = Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) end @@ -120,7 +120,7 @@ defmodule SetPluginsCommandTest do %{ mode: :online, started: [], - stopped: [:rabbitmq_federation], + stopped: [:rabbitmq_federation_common], set: [:rabbitmq_stomp] } ] = Enum.to_list(test_stream0) @@ -130,21 +130,21 @@ defmodule SetPluginsCommandTest do assert [:amqp_client, :rabbitmq_stomp] = Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) - assert {:stream, test_stream1} = @command.run(["rabbitmq_federation"], context[:opts]) + assert {:stream, test_stream1} = @command.run(["rabbitmq_federation_common"], context[:opts]) assert [ - [:rabbitmq_federation], + [:rabbitmq_federation_common], %{ mode: :online, - started: [:rabbitmq_federation], + started: [:rabbitmq_federation_common], stopped: [:rabbitmq_stomp], - set: [:rabbitmq_federation] + set: [:rabbitmq_federation_common] } ] = Enum.to_list(test_stream1) - assert {:ok, [[:rabbitmq_federation]]} = :file.consult(context[:opts][:enabled_plugins_file]) + assert {:ok, [[:rabbitmq_federation_common]]} = :file.consult(context[:opts][:enabled_plugins_file]) - assert [:amqp_client, :rabbitmq_federation] = + assert [:amqp_client, :rabbitmq_federation_common] = Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) end @@ -156,7 +156,7 @@ defmodule SetPluginsCommandTest do %{ mode: :online, started: [], - stopped: [:rabbitmq_federation, :rabbitmq_stomp], + stopped: [:rabbitmq_federation_common, :rabbitmq_stomp], set: [] } ] = Enum.to_list(test_stream) @@ -171,22 +171,22 @@ defmodule SetPluginsCommandTest do set_enabled_plugins([], :online, get_rabbit_hostname(), context[:opts]) assert {:stream, test_stream} = - @command.run(["rabbitmq_federation", "rabbitmq_stomp"], context[:opts]) + @command.run(["rabbitmq_federation_common", "rabbitmq_stomp"], context[:opts]) assert [ - [:rabbitmq_federation, :rabbitmq_stomp], + [:rabbitmq_federation_common, :rabbitmq_stomp], %{ mode: :online, - started: [:rabbitmq_federation, :rabbitmq_stomp], + started: [:rabbitmq_federation_common, :rabbitmq_stomp], stopped: [], - set: [:rabbitmq_federation, :rabbitmq_stomp] + set: [:rabbitmq_federation_common, :rabbitmq_stomp] } ] = Enum.to_list(test_stream) - assert {:ok, [[:rabbitmq_federation, :rabbitmq_stomp]]} = + assert {:ok, [[:rabbitmq_federation_common, :rabbitmq_stomp]]} = :file.consult(context[:opts][:enabled_plugins_file]) - assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] = + assert [:amqp_client, :rabbitmq_federation_common, :rabbitmq_stomp] = Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])) end diff --git a/deps/rabbitmq_cli/test/test_helper.exs b/deps/rabbitmq_cli/test/test_helper.exs index d7f218715530..6ab840acb504 100644 --- a/deps/rabbitmq_cli/test/test_helper.exs +++ b/deps/rabbitmq_cli/test/test_helper.exs @@ -814,13 +814,13 @@ defmodule TestHelper do plugins = currently_active_plugins(%{opts: %{node: node}}) - case Enum.member?(plugins, :rabbitmq_federation) do + case Enum.member?(plugins, :rabbitmq_federation_common) do true -> :ok false -> set_enabled_plugins( - plugins ++ [:rabbitmq_federation], + plugins ++ [:rabbitmq_federation_common], :online, get_rabbit_hostname(), opts diff --git a/deps/rabbitmq_exchange_federation/Makefile b/deps/rabbitmq_exchange_federation/Makefile new file mode 100644 index 000000000000..eb0a51622e9f --- /dev/null +++ b/deps/rabbitmq_exchange_federation/Makefile @@ -0,0 +1,25 @@ +PROJECT = rabbitmq_exchange_federation +PROJECT_DESCRIPTION = RabbitMQ Exchange Federation +PROJECT_MOD = rabbit_exchange_federation_app + +define PROJECT_ENV +[ + {pgroup_name_cluster_id, false}, + {internal_exchange_check_interval, 90000} + ] +endef + +define PROJECT_APP_EXTRA_KEYS + {broker_version_requirements, []} +endef + +DEPS = rabbit_common rabbit amqp_client rabbitmq_federation_common +TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers + +PLT_APPS += rabbitmq_cli + +DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk +DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk + +include ../../rabbitmq-components.mk +include ../../erlang.mk diff --git a/deps/rabbitmq_federation/README-hacking b/deps/rabbitmq_exchange_federation/README-hacking similarity index 100% rename from deps/rabbitmq_federation/README-hacking rename to deps/rabbitmq_exchange_federation/README-hacking diff --git a/deps/rabbitmq_exchange_federation/README.md b/deps/rabbitmq_exchange_federation/README.md new file mode 100644 index 000000000000..d96c13a02e57 --- /dev/null +++ b/deps/rabbitmq_exchange_federation/README.md @@ -0,0 +1,23 @@ +## RabbitMQ Federation + +RabbitMQ federation offers a group of features for loosely +coupled and WAN-friendly distributed RabbitMQ setups. Note that +this is not an alternative to queue mirroring. + + +## Supported RabbitMQ Versions + +This plugin ships with RabbitMQ, there is no need to +install it separately. + + +## Documentation + +See [RabbitMQ federation plugin](https://www.rabbitmq.com/federation.html) on rabbitmq.com. + + +## License and Copyright + +Released under [the same license as RabbitMQ](https://www.rabbitmq.com/mpl.html). + +2007-2015 (c) 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. diff --git a/deps/rabbitmq_exchange_federation/include/rabbit_exchange_federation.hrl b/deps/rabbitmq_exchange_federation/include/rabbit_exchange_federation.hrl new file mode 100644 index 000000000000..e8ddecc7614e --- /dev/null +++ b/deps/rabbitmq_exchange_federation/include/rabbit_exchange_federation.hrl @@ -0,0 +1,8 @@ +%% 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. +%% + +-define(FEDERATION_PG_SCOPE, rabbitmq_exchange_federation_pg_scope). diff --git a/deps/rabbitmq_exchange_federation/src/rabbit_exchange_federation_app.erl b/deps/rabbitmq_exchange_federation/src/rabbit_exchange_federation_app.erl new file mode 100644 index 000000000000..dfdc0677d10b --- /dev/null +++ b/deps/rabbitmq_exchange_federation/src/rabbit_exchange_federation_app.erl @@ -0,0 +1,51 @@ +%% 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_exchange_federation_app). + +-include_lib("rabbitmq_federation_common/include/rabbit_federation.hrl"). +-include("rabbit_exchange_federation.hrl"). + +-behaviour(application). +-export([start/2, stop/1]). + +%% Dummy supervisor - see Ulf Wiger's comment at +%% http://erlang.org/pipermail/erlang-questions/2010-April/050508.html + +%% All of our actual server processes are supervised by +%% rabbit_federation_sup, which is started by a rabbit_boot_step +%% (since it needs to start up before queue / exchange recovery, so it +%% can't be part of our application). +%% +%% However, we still need an application behaviour since we need to +%% know when our application has started since then the Erlang client +%% will have started and we can therefore start our links going. Since +%% the application behaviour needs a tree of processes to supervise, +%% this is it... +-behaviour(supervisor). +-export([init/1]). + +start(_Type, _StartArgs) -> + ets:insert(?FEDERATION_ETS, + {rabbitmq_exchange_federation, + #{link_module => rabbit_federation_exchange_link_sup_sup}}), + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +stop(_State) -> + ets:delete(?FEDERATION_ETS, rabbitmq_exchange_federation), + rabbit_federation_pg:stop_scope(?FEDERATION_PG_SCOPE), + ok. + +%%---------------------------------------------------------------------------- + +init([]) -> + Flags = #{ + strategy => one_for_one, + intensity => 3, + period => 10 + }, + {ok, {Flags, []}}. diff --git a/deps/rabbitmq_exchange_federation/src/rabbit_exchange_federation_sup.erl b/deps/rabbitmq_exchange_federation/src/rabbit_exchange_federation_sup.erl new file mode 100644 index 000000000000..886435630e99 --- /dev/null +++ b/deps/rabbitmq_exchange_federation/src/rabbit_exchange_federation_sup.erl @@ -0,0 +1,64 @@ +%% 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_exchange_federation_sup). + +-behaviour(supervisor). + +%% Supervises everything. There is just one of these. + +-include_lib("rabbit_common/include/rabbit.hrl"). +-include("rabbit_exchange_federation.hrl"). + +-define(SUPERVISOR, ?MODULE). + +-export([start_link/0, stop/0]). + +-export([init/1]). + +%% This supervisor needs to be part of the rabbit application since +%% a) it needs to be in place when exchange recovery takes place +%% b) it needs to go up and down with rabbit + +-rabbit_boot_step({rabbit_exchange_federation_supervisor, + [{description, "federation"}, + {mfa, {rabbit_sup, start_child, [?MODULE]}}, + {requires, [kernel_ready, rabbit_federation_supervisor]}, + {cleanup, {?MODULE, stop, []}}, + {enables, rabbit_federation_exchange}]}). + +%%---------------------------------------------------------------------------- + +start_link() -> + supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []). + +stop() -> + ok = supervisor:terminate_child(rabbit_sup, ?MODULE), + ok = supervisor:delete_child(rabbit_sup, ?MODULE). + +%%---------------------------------------------------------------------------- + +init([]) -> + XLinkSupSup = #{ + id => x_links, + start => {rabbit_federation_exchange_link_sup_sup, start_link, []}, + restart => transient, + shutdown => ?SUPERVISOR_WAIT, + type => supervisor, + modules =>[rabbit_federation_exchange_link_sup_sup] + }, + %% with default reconnect-delay of 5 second, this supports up to + %% 100 links constantly failing and being restarted a minute + %% (or 200 links if reconnect-delay is 10 seconds, 600 with 30 seconds, + %% etc: N * (60/reconnect-delay) <= 1200) + Flags = #{ + strategy => one_for_one, + intensity => 1200, + period => 60 + }, + Specs = [XLinkSupSup], + {ok, {Flags, Specs}}. diff --git a/deps/rabbitmq_federation/src/rabbit_federation_exchange.erl b/deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange.erl similarity index 98% rename from deps/rabbitmq_federation/src/rabbit_federation_exchange.erl rename to deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange.erl index 52931042ae69..cc41a22b6edf 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_exchange.erl +++ b/deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange.erl @@ -9,6 +9,7 @@ -module(rabbit_federation_exchange). -include_lib("amqp_client/include/amqp_client.hrl"). +-include("rabbit_exchange_federation.hrl"). -rabbit_boot_step({?MODULE, [{description, "federation exchange decorator"}, diff --git a/deps/rabbitmq_federation/src/rabbit_federation_exchange_link.erl b/deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange_link.erl similarity index 99% rename from deps/rabbitmq_federation/src/rabbit_federation_exchange_link.erl rename to deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange_link.erl index 3509a7b2fd89..81d8a493335f 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_exchange_link.erl +++ b/deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange_link.erl @@ -8,7 +8,8 @@ -module(rabbit_federation_exchange_link). -include_lib("amqp_client/include/amqp_client.hrl"). --include("rabbit_federation.hrl"). +-include_lib("rabbitmq_federation_common/include/rabbit_federation.hrl"). +-include("rabbit_exchange_federation.hrl"). -behaviour(gen_server2). @@ -49,7 +50,7 @@ %% and the Erlang client is not running. This then gets invoked when %% the federation app is started. go() -> - _ = rabbit_federation_pg:start_scope(), + _ = rabbit_federation_pg:start_scope(?FEDERATION_PG_SCOPE), cast(go). add_binding(S, XN, B) -> cast(XN, {enqueue, S, {add_binding, B}}). @@ -448,7 +449,7 @@ go(S0 = {not_started, {Upstream, UParams, DownXName}}) -> %% serial we will process. Since it compares larger than %% any number we never process any commands. And we will %% soon get told to stop anyway. - {ok, Interval} = application:get_env(rabbitmq_federation, + {ok, Interval} = application:get_env(rabbitmq_exchange_federation, internal_exchange_check_interval), State = ensure_upstream_bindings( consume_from_upstream_queue( diff --git a/deps/rabbitmq_federation/src/rabbit_federation_exchange_link_sup_sup.erl b/deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange_link_sup_sup.erl similarity index 85% rename from deps/rabbitmq_federation/src/rabbit_federation_exchange_link_sup_sup.erl rename to deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange_link_sup_sup.erl index e1a962afb5b2..4371fb0f0b7c 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_exchange_link_sup_sup.erl +++ b/deps/rabbitmq_exchange_federation/src/rabbit_federation_exchange_link_sup_sup.erl @@ -10,6 +10,7 @@ -behaviour(mirrored_supervisor). -include_lib("rabbit_common/include/rabbit.hrl"). +-include("rabbit_exchange_federation.hrl"). -define(SUPERVISOR, ?MODULE). %% Supervises the upstream links for all exchanges (but not queues). We need @@ -26,7 +27,7 @@ start_link() -> %% This scope is used by concurrently starting exchange and queue links, %% and other places, so we have to start it very early outside of the supervision tree. %% The scope is stopped in stop/1. - _ = rabbit_federation_pg:start_scope(), + _ = rabbit_federation_pg:start_scope(?FEDERATION_PG_SCOPE), mirrored_supervisor:start_link({local, ?SUPERVISOR}, ?SUPERVISOR, ?MODULE, []). @@ -35,7 +36,8 @@ start_link() -> start_child(X) -> case mirrored_supervisor:start_child( ?SUPERVISOR, - {id(X), {rabbit_federation_link_sup, start_link, [X]}, + {id(X), {rabbit_federation_link_sup, start_link, + [rabbit_federation_exchange_link, X]}, transient, ?SUPERVISOR_WAIT, supervisor, [rabbit_federation_link_sup]}) of {ok, _Pid} -> ok; @@ -49,12 +51,14 @@ start_child(X) -> end. adjust({clear_upstream, VHost, UpstreamName}) -> - _ = [rabbit_federation_link_sup:adjust(Pid, X, {clear_upstream, UpstreamName}) || + _ = [rabbit_federation_link_sup:adjust(Pid, rabbit_federation_exchange_link, X, + {clear_upstream, UpstreamName}) || {#exchange{name = Name} = X, Pid, _, _} <- mirrored_supervisor:which_children(?SUPERVISOR), Name#resource.virtual_host == VHost], ok; adjust(Reason) -> - _ = [rabbit_federation_link_sup:adjust(Pid, X, Reason) || + _ = [rabbit_federation_link_sup:adjust(Pid, rabbit_federation_exchange_link, + X, Reason) || {X, Pid, _, _} <- mirrored_supervisor:which_children(?SUPERVISOR)], ok. diff --git a/deps/rabbitmq_federation/src/rabbit_federation_upstream_exchange.erl b/deps/rabbitmq_exchange_federation/src/rabbit_federation_upstream_exchange.erl similarity index 96% rename from deps/rabbitmq_federation/src/rabbit_federation_upstream_exchange.erl rename to deps/rabbitmq_exchange_federation/src/rabbit_federation_upstream_exchange.erl index b53e4ccfad45..23e4de27ce22 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_upstream_exchange.erl +++ b/deps/rabbitmq_exchange_federation/src/rabbit_federation_upstream_exchange.erl @@ -17,7 +17,8 @@ {enables, recovery}]}). -include_lib("rabbit_common/include/rabbit.hrl"). --include("rabbit_federation.hrl"). +-include_lib("rabbitmq_federation_common/include/rabbit_federation.hrl"). +-include("rabbit_exchange_federation.hrl"). -behaviour(rabbit_exchange_type). diff --git a/deps/rabbitmq_federation/test/definition_import_SUITE.erl b/deps/rabbitmq_exchange_federation/test/definition_import_SUITE.erl similarity index 65% rename from deps/rabbitmq_federation/test/definition_import_SUITE.erl rename to deps/rabbitmq_exchange_federation/test/definition_import_SUITE.erl index d423849090ed..d656d187f1e1 100644 --- a/deps/rabbitmq_federation/test/definition_import_SUITE.erl +++ b/deps/rabbitmq_exchange_federation/test/definition_import_SUITE.erl @@ -93,16 +93,6 @@ export(Config) -> run_export() -> rabbit_definitions:all_definitions(). -run_directory_import_case(Path, Expected) -> - ct:pal("Will load definitions from files under ~tp~n", [Path]), - Result = rabbit_definitions:maybe_load_definitions_from(true, Path), - case Expected of - ok -> - ok = Result; - error -> - ?assertMatch({error, {failed_to_import_definitions, _, _}}, Result) - end. - run_import_case(Path) -> {ok, Body} = file:read_file(Path), ct:pal("Successfully loaded a definition to import from ~tp~n", [Path]), @@ -112,35 +102,3 @@ run_import_case(Path) -> ct:pal("Import case ~tp failed: ~tp~n", [Path, E]), ct:fail({expected_failure, Path, E}) end. - -run_invalid_import_case(Path) -> - {ok, Body} = file:read_file(Path), - ct:pal("Successfully loaded a definition file at ~tp~n", [Path]), - case rabbit_definitions:import_raw(Body) of - ok -> - ct:pal("Expected import case ~tp to fail~n", [Path]), - ct:fail({expected_failure, Path}); - {error, _E} -> ok - end. - -run_invalid_import_case_if_unchanged(Path) -> - Mod = rabbit_definitions_import_local_filesystem, - ct:pal("Successfully loaded a definition to import from ~tp~n", [Path]), - case rabbit_definitions:maybe_load_definitions_from_local_filesystem_if_unchanged(Mod, false, Path) of - ok -> - ct:pal("Expected import case ~tp to fail~n", [Path]), - ct:fail({expected_failure, Path}); - {error, _E} -> ok - end. - -queue_lookup(Config, VHost, Name) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_amqqueue, lookup, [rabbit_misc:r(VHost, queue, Name)]). - -vhost_lookup(Config, VHost) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_vhost, lookup, [VHost]). - -user_lookup(Config, User) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_auth_backend_internal, lookup_user, [User]). - -delete_vhost(Config, VHost) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_vhost, delete, [VHost, <<"CT tests">>]). diff --git a/deps/rabbitmq_federation/test/definition_import_SUITE_data/case1.json b/deps/rabbitmq_exchange_federation/test/definition_import_SUITE_data/case1.json similarity index 100% rename from deps/rabbitmq_federation/test/definition_import_SUITE_data/case1.json rename to deps/rabbitmq_exchange_federation/test/definition_import_SUITE_data/case1.json diff --git a/deps/rabbitmq_federation/test/exchange_SUITE.erl b/deps/rabbitmq_exchange_federation/test/exchange_SUITE.erl similarity index 80% rename from deps/rabbitmq_federation/test/exchange_SUITE.erl rename to deps/rabbitmq_exchange_federation/test/exchange_SUITE.erl index 58d617b5def1..2b65fb731082 100644 --- a/deps/rabbitmq_federation/test/exchange_SUITE.erl +++ b/deps/rabbitmq_exchange_federation/test/exchange_SUITE.erl @@ -11,20 +11,12 @@ -include_lib("rabbitmq_ct_helpers/include/rabbit_assert.hrl"). -include_lib("amqp_client/include/amqp_client.hrl"). --include("rabbit_federation.hrl"). +-include("rabbit_exchange_federation.hrl"). -compile(export_all). -import(rabbit_federation_test_util, - [expect/3, expect/4, expect_empty/2, - set_upstream/4, set_upstream/5, set_upstream_in_vhost/5, set_upstream_in_vhost/6, - clear_upstream/3, set_upstream_set/4, - set_policy/5, set_policy_pattern/5, clear_policy/3, - set_policy_upstream/5, set_policy_upstreams/4, - all_federation_links/2, federation_links_in_vhost/3, status_fields/2]). - --import(rabbit_ct_broker_helpers, - [set_policy_in_vhost/7]). + [expect/3, expect/4, expect_empty/2]). all() -> [ @@ -543,7 +535,8 @@ exchange_federation_link_status(Config) -> await_binding(Config, 0, UpX, RK), [Link] = rabbit_ct_broker_helpers:rpc(Config, 0, - rabbit_federation_status, status, []), + rabbit_federation_status, status, + []), true = is_binary(proplists:get_value(id, Link)), clean_up_federation_related_bits(Config). @@ -586,116 +579,122 @@ lookup_exchange_status(Config) -> clean_up_federation_related_bits(Config). child_id_format(Config) -> - [UpstreamNode, - OldNodeA, - NewNodeB, - OldNodeC, - NewNodeD] = rabbit_ct_broker_helpers:get_node_configs( - Config, nodename), - - %% Create a cluster with the nodes running the old version of RabbitMQ in - %% mixed-version testing. - %% - %% Note: we build this on the assumption that `rabbit_ct_broker_helpers' - %% starts nodes this way: - %% Node 1: the primary copy of RabbitMQ the test is started from - %% Node 2: the secondary umbrella (if any) - %% Node 3: the primary copy - %% Node 4: the secondary umbrella - %% ... - %% - %% Therefore, `UpstreamNode' will use the primary copy, `OldNodeA' the - %% secondary umbrella, `NewNodeB' the primary copy, and so on. - Config1 = rabbit_ct_broker_helpers:cluster_nodes( - Config, [OldNodeA, OldNodeC]), - - %% Prepare the whole federated exchange on that old cluster. - UpstreamName = <<"fed_on_upgrade">>, - rabbit_ct_broker_helpers:set_parameter( - Config1, OldNodeA, <<"federation-upstream">>, UpstreamName, - [ - {<<"uri">>, rabbit_ct_broker_helpers:node_uri(Config1, UpstreamNode)} - ]), - - rabbit_ct_broker_helpers:set_policy( - Config1, OldNodeA, - <<"fed_on_upgrade_policy">>, <<"^fed_">>, <<"all">>, - [ - {<<"federation-upstream-pattern">>, UpstreamName} - ]), - - XName = <<"fed_ex_on_upgrade_cluster">>, - X = exchange_declare_method(XName, <<"direct">>), - {Conn1, Ch1} = rabbit_ct_client_helpers:open_connection_and_channel( - Config1, OldNodeA), - ?assertEqual({'exchange.declare_ok'}, declare_exchange(Ch1, X)), - rabbit_ct_client_helpers:close_channel(Ch1), - rabbit_ct_client_helpers:close_connection(Conn1), - - %% Verify the format of the child ID. In the main branch, the format was - %% temporarily a size-2 tuple with a list as the first element. This was - %% not kept later and the original ID format is used in old and new nodes. - [{Id, _, _, _}] = rabbit_ct_broker_helpers:rpc( - Config1, OldNodeA, - mirrored_supervisor, which_children, - [rabbit_federation_exchange_link_sup_sup]), - case Id of - %% This is the format we expect everywhere. - #exchange{name = #resource{name = XName}} -> - %% Verify that the supervisors exist on all nodes. - lists:foreach( - fun(Node) -> - ?assertMatch( - [{#exchange{name = #resource{name = XName}}, - _, _, _}], - rabbit_ct_broker_helpers:rpc( - Config1, Node, - mirrored_supervisor, which_children, - [rabbit_federation_exchange_link_sup_sup])) - end, [OldNodeA, OldNodeC]), - - %% Simulate a rolling upgrade by: - %% 1. adding new nodes to the old cluster - %% 2. stopping the old nodes - %% - %% After that, the supervisors run on the new code. - Config2 = rabbit_ct_broker_helpers:cluster_nodes( - Config1, OldNodeA, [NewNodeB, NewNodeD]), - ok = rabbit_ct_broker_helpers:stop_broker(Config2, OldNodeA), - ok = rabbit_ct_broker_helpers:reset_node(Config1, OldNodeA), - ok = rabbit_ct_broker_helpers:stop_broker(Config2, OldNodeC), - ok = rabbit_ct_broker_helpers:reset_node(Config2, OldNodeC), - - %% Verify that the supervisors still use the same IDs. - lists:foreach( - fun(Node) -> - ?assertMatch( - [{#exchange{name = #resource{name = XName}}, - _, _, _}], - rabbit_ct_broker_helpers:rpc( - Config2, Node, - mirrored_supervisor, which_children, - [rabbit_federation_exchange_link_sup_sup])) - end, [NewNodeB, NewNodeD]), - - %% Delete the exchange: it should work because the ID format is the - %% one expected. - %% - %% During the transient period where the ID format was changed, - %% this would crash with a badmatch because the running - %% supervisor's ID would not match the content of the database. - {Conn2, Ch2} = rabbit_ct_client_helpers:open_connection_and_channel( - Config2, NewNodeB), - ?assertEqual({'exchange.delete_ok'}, delete_exchange(Ch2, XName)), - rabbit_ct_client_helpers:close_channel(Ch2), - rabbit_ct_client_helpers:close_connection(Conn2); - - %% This is the transient format we are not interested in as it only - %% lived in a development branch. - {List, #exchange{name = #resource{name = XName}}} - when is_list(List) -> - {skip, "Testcase skipped with the transiently changed ID format"} - end. + case rabbit_ct_helpers:is_mixed_versions() of + false -> + [UpstreamNode, + OldNodeA, + NewNodeB, + OldNodeC, + NewNodeD] = rabbit_ct_broker_helpers:get_node_configs( + Config, nodename), + + %% Create a cluster with the nodes running the old version of RabbitMQ in + %% mixed-version testing. + %% + %% Note: we build this on the assumption that `rabbit_ct_broker_helpers' + %% starts nodes this way: + %% Node 1: the primary copy of RabbitMQ the test is started from + %% Node 2: the secondary umbrella (if any) + %% Node 3: the primary copy + %% Node 4: the secondary umbrella + %% ... + %% + %% Therefore, `UpstreamNode' will use the primary copy, `OldNodeA' the + %% secondary umbrella, `NewNodeB' the primary copy, and so on. + Config1 = rabbit_ct_broker_helpers:cluster_nodes( + Config, [OldNodeA, OldNodeC]), + + %% Prepare the whole federated exchange on that old cluster. + UpstreamName = <<"fed_on_upgrade">>, + rabbit_ct_broker_helpers:set_parameter( + Config1, OldNodeA, <<"federation-upstream">>, UpstreamName, + [ + {<<"uri">>, rabbit_ct_broker_helpers:node_uri(Config1, UpstreamNode)} + ]), + + rabbit_ct_broker_helpers:set_policy( + Config1, OldNodeA, + <<"fed_on_upgrade_policy">>, <<"^fed_">>, <<"all">>, + [ + {<<"federation-upstream-pattern">>, UpstreamName} + ]), + + XName = <<"fed_ex_on_upgrade_cluster">>, + X = exchange_declare_method(XName, <<"direct">>), + {Conn1, Ch1} = rabbit_ct_client_helpers:open_connection_and_channel( + Config1, OldNodeA), + ?assertEqual({'exchange.declare_ok'}, declare_exchange(Ch1, X)), + rabbit_ct_client_helpers:close_channel(Ch1), + rabbit_ct_client_helpers:close_connection(Conn1), + + %% Verify the format of the child ID. In the main branch, the format was + %% temporarily a size-2 tuple with a list as the first element. This was + %% not kept later and the original ID format is used in old and new nodes. + [{Id, _, _, _}] = rabbit_ct_broker_helpers:rpc( + Config1, OldNodeA, + mirrored_supervisor, which_children, + [rabbit_federation_exchange_link_sup_sup]), + case Id of + %% This is the format we expect everywhere. + #exchange{name = #resource{name = XName}} -> + %% Verify that the supervisors exist on all nodes. + lists:foreach( + fun(Node) -> + ?assertMatch( + [{#exchange{name = #resource{name = XName}}, + _, _, _}], + rabbit_ct_broker_helpers:rpc( + Config1, Node, + mirrored_supervisor, which_children, + [rabbit_federation_exchange_link_sup_sup])) + end, [OldNodeA, OldNodeC]), + + %% Simulate a rolling upgrade by: + %% 1. adding new nodes to the old cluster + %% 2. stopping the old nodes + %% + %% After that, the supervisors run on the new code. + Config2 = rabbit_ct_broker_helpers:cluster_nodes( + Config1, OldNodeA, [NewNodeB, NewNodeD]), + ok = rabbit_ct_broker_helpers:stop_broker(Config2, OldNodeA), + ok = rabbit_ct_broker_helpers:reset_node(Config1, OldNodeA), + ok = rabbit_ct_broker_helpers:stop_broker(Config2, OldNodeC), + ok = rabbit_ct_broker_helpers:reset_node(Config2, OldNodeC), + + %% Verify that the supervisors still use the same IDs. + lists:foreach( + fun(Node) -> + ?assertMatch( + [{#exchange{name = #resource{name = XName}}, + _, _, _}], + rabbit_ct_broker_helpers:rpc( + Config2, Node, + mirrored_supervisor, which_children, + [rabbit_federation_exchange_link_sup_sup])) + end, [NewNodeB, NewNodeD]), + + %% Delete the exchange: it should work because the ID format is the + %% one expected. + %% + %% During the transient period where the ID format was changed, + %% this would crash with a badmatch because the running + %% supervisor's ID would not match the content of the database. + {Conn2, Ch2} = rabbit_ct_client_helpers:open_connection_and_channel( + Config2, NewNodeB), + ?assertEqual({'exchange.delete_ok'}, delete_exchange(Ch2, XName)), + rabbit_ct_client_helpers:close_channel(Ch2), + rabbit_ct_client_helpers:close_connection(Conn2); + + %% This is the transient format we are not interested in as it only + %% lived in a development branch. + {List, #exchange{name = #resource{name = XName}}} + when is_list(List) -> + {skip, "Testcase skipped with the transiently changed ID format"} + end; + true -> + %% skip the test in mixed version mode + {skip, "Should not run in mixed version environments"} + end. %% %% Test helpers @@ -749,7 +748,7 @@ delete_exchanges(Ch, Frames) -> [delete_exchange(Ch, X) || #'exchange.declare'{exchange = X} <- Frames]. declare_exchange(Ch, X) -> - amqp_channel:call(Ch, X). + #'exchange.declare_ok'{} = amqp_channel:call(Ch, X). declare_queue(Ch) -> #'queue.declare_ok'{queue = Q} = diff --git a/deps/rabbitmq_exchange_federation/test/exchange_federation_status_command_SUITE.erl b/deps/rabbitmq_exchange_federation/test/exchange_federation_status_command_SUITE.erl new file mode 100644 index 000000000000..50b97f9199bc --- /dev/null +++ b/deps/rabbitmq_exchange_federation/test/exchange_federation_status_command_SUITE.erl @@ -0,0 +1,168 @@ +%% 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(exchange_federation_status_command_SUITE). + +-include_lib("amqp_client/include/amqp_client.hrl"). + +-compile(export_all). + +-define(CMD, 'Elixir.RabbitMQ.CLI.Ctl.Commands.FederationStatusCommand'). + +all() -> + [ + {group, not_federated}, + {group, federated}, + {group, federated_down} + ]. + +groups() -> + [ + {not_federated, [], [ + run_not_federated, + output_not_federated + ]}, + {federated, [], [ + run_federated, + output_federated + ]}, + {federated_down, [], [ + run_down_federated + ]} + ]. + +%% ------------------------------------------------------------------- +%% Testsuite setup/teardown. +%% ------------------------------------------------------------------- + +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + Config1 = rabbit_ct_helpers:set_config(Config, [ + {rmq_nodename_suffix, ?MODULE} + ]), + Config2 = rabbit_ct_helpers:run_setup_steps(Config1, + rabbit_ct_broker_helpers:setup_steps() ++ + rabbit_ct_client_helpers:setup_steps()), + Config2. + +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(federated, Config) -> + rabbit_federation_test_util:setup_federation(Config), + Config; +init_per_group(federated_down, Config) -> + rabbit_federation_test_util:setup_down_federation(Config), + Config; +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. +%% ------------------------------------------------------------------- +run_not_federated(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + {stream, []} = ?CMD:run([], Opts#{only_down => false}). + +output_not_federated(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + {stream, []} = ?CMD:output({stream, []}, Opts). + +run_federated(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + %% All + rabbit_federation_test_util:with_ch( + Config, + fun(_) -> + timer:sleep(3000), + {stream, [Props]} = ?CMD:run([], Opts#{only_down => false}), + <<"upstream">> = proplists:get_value(upstream_exchange, Props), + <<"fed1.downstream">> = proplists:get_value(exchange, Props), + exchange = proplists:get_value(type, Props), + running = proplists:get_value(status, Props) + end, + [rabbit_federation_test_util:x(<<"fed1.downstream">>)]), + %% Down + rabbit_federation_test_util:with_ch( + Config, + fun(_) -> + {stream, []} = ?CMD:run([], Opts#{only_down => true}) + end, + [rabbit_federation_test_util:x(<<"fed1.downstream">>)]). + +run_down_federated(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + %% All + rabbit_federation_test_util:with_ch( + Config, + fun(_) -> + rabbit_ct_helpers:await_condition( + fun() -> + {stream, ManyProps} = ?CMD:run([], Opts#{only_down => false}), + Links = [{proplists:get_value(upstream, Props), + proplists:get_value(status, Props)} + || Props <- ManyProps], + [{<<"broken-bunny">>, error}, {<<"localhost">>, running}] + == lists:sort(Links) + end, 15000) + end, + [rabbit_federation_test_util:x(<<"fed1.downstream">>)]), + %% Down + rabbit_federation_test_util:with_ch( + Config, + fun(_) -> + rabbit_ct_helpers:await_condition( + fun() -> + {stream, Props} = ?CMD:run([], Opts#{only_down => true}), + (length(Props) == 1) + andalso (<<"broken-bunny">> == proplists:get_value(upstream, hd(Props))) + andalso (error == proplists:get_value(status, hd(Props))) + end, 15000) + end, + [rabbit_federation_test_util:x(<<"fed1.downstream">>)]). + +output_federated(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + Input = {stream,[[{queue, <<"fed1.downstream">>}, + {consumer_tag, <<"fed.tag">>}, + {upstream_queue, <<"upstream">>}, + {type, queue}, + {vhost, <<"/">>}, + {upstream, <<"localhost">>}, + {status, running}, + {local_connection, <<"">>}, + {uri, <<"amqp://localhost:21000">>}, + {timestamp, {{2016,11,21},{8,51,19}}}]]}, + {stream, [#{queue := <<"fed1.downstream">>, + upstream_queue := <<"upstream">>, + type := queue, + vhost := <<"/">>, + upstream := <<"localhost">>, + status := running, + local_connection := <<"">>, + uri := <<"amqp://localhost:21000">>, + last_changed := <<"2016-11-21 08:51:19">>, + exchange := <<>>, + upstream_exchange := <<>>, + error := <<>>}]} + = ?CMD:output(Input, Opts). diff --git a/deps/rabbitmq_exchange_federation/test/rabbit_federation_test_util.erl b/deps/rabbitmq_exchange_federation/test/rabbit_federation_test_util.erl new file mode 100644 index 000000000000..60a99370001b --- /dev/null +++ b/deps/rabbitmq_exchange_federation/test/rabbit_federation_test_util.erl @@ -0,0 +1,246 @@ +%% 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_federation_test_util). + +-include("rabbit_exchange_federation.hrl"). +-include_lib("rabbitmq_federation_common/include/rabbit_federation.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("amqp_client/include/amqp_client.hrl"). + +-compile(export_all). + +-import(rabbit_misc, [pget/2]). + +setup_federation(Config) -> + setup_federation_with_upstream_params(Config, []). + +setup_federation_with_upstream_params(Config, ExtraParams) -> + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream">>, <<"localhost">>, [ + {<<"uri">>, rabbit_ct_broker_helpers:node_uri(Config, 0)}, + {<<"consumer-tag">>, <<"fed.tag">>} + ] ++ ExtraParams), + + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream">>, <<"local5673">>, [ + {<<"uri">>, <<"amqp://localhost:1">>} + ] ++ ExtraParams), + + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream-set">>, <<"upstream">>, [ + [ + {<<"upstream">>, <<"localhost">>}, + {<<"exchange">>, <<"upstream">>}, + {<<"queue">>, <<"upstream">>} + ] + ]), + + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream-set">>, <<"upstream2">>, [ + [ + {<<"upstream">>, <<"localhost">>}, + {<<"exchange">>, <<"upstream2">>}, + {<<"queue">>, <<"upstream2">>} + ] + ]), + + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream-set">>, <<"localhost">>, [ + [{<<"upstream">>, <<"localhost">>}] + ]), + + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream-set">>, <<"upstream12">>, [ + [ + {<<"upstream">>, <<"localhost">>}, + {<<"exchange">>, <<"upstream">>}, + {<<"queue">>, <<"upstream">>} + ], [ + {<<"upstream">>, <<"localhost">>}, + {<<"exchange">>, <<"upstream2">>}, + {<<"queue">>, <<"upstream2">>} + ] + ]), + + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream-set">>, <<"one">>, [ + [ + {<<"upstream">>, <<"localhost">>}, + {<<"exchange">>, <<"one">>}, + {<<"queue">>, <<"one">>} + ] + ]), + + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream-set">>, <<"two">>, [ + [ + {<<"upstream">>, <<"localhost">>}, + {<<"exchange">>, <<"two">>}, + {<<"queue">>, <<"two">>} + ] + ]), + + rabbit_ct_broker_helpers:set_parameter(Config, 0, + <<"federation-upstream-set">>, <<"upstream5673">>, [ + [ + {<<"upstream">>, <<"local5673">>}, + {<<"exchange">>, <<"upstream">>} + ] + ]), + + rabbit_ct_broker_helpers:rpc( + Config, 0, rabbit_policy, set, + [<<"/">>, <<"fed">>, <<"^fed1\.">>, [{<<"federation-upstream-set">>, <<"upstream">>}], + 0, <<"all">>, <<"acting-user">>]), + + rabbit_ct_broker_helpers:rpc( + Config, 0, rabbit_policy, set, + [<<"/">>, <<"fed2">>, <<"^fed2\.">>, [{<<"federation-upstream-set">>, <<"upstream2">>}], + 0, <<"all">>, <<"acting-user">>]), + + rabbit_ct_broker_helpers:rpc( + Config, 0, rabbit_policy, set, + [<<"/">>, <<"fed12">>, <<"^fed3\.">>, [{<<"federation-upstream-set">>, <<"upstream12">>}], + 2, <<"all">>, <<"acting-user">>]), + + rabbit_ct_broker_helpers:set_policy(Config, 0, + <<"one">>, <<"^two$">>, <<"all">>, [ + {<<"federation-upstream-set">>, <<"one">>}]), + + rabbit_ct_broker_helpers:set_policy(Config, 0, + <<"two">>, <<"^one$">>, <<"all">>, [ + {<<"federation-upstream-set">>, <<"two">>}]), + + rabbit_ct_broker_helpers:set_policy(Config, 0, + <<"hare">>, <<"^hare\.">>, <<"all">>, [ + {<<"federation-upstream-set">>, <<"upstream5673">>}]), + + rabbit_ct_broker_helpers:set_policy(Config, 0, + <<"all">>, <<"^all\.">>, <<"all">>, [ + {<<"federation-upstream-set">>, <<"all">>}]), + + rabbit_ct_broker_helpers:set_policy(Config, 0, + <<"new">>, <<"^new\.">>, <<"all">>, [ + {<<"federation-upstream-set">>, <<"new-set">>}]), + Config. + +setup_down_federation(Config) -> + rabbit_ct_broker_helpers:set_parameter( + Config, 0, <<"federation-upstream">>, <<"broken-bunny">>, + [{<<"uri">>, <<"amqp://broken-bunny">>}, + {<<"reconnect-delay">>, 600000}]), + rabbit_ct_broker_helpers:set_parameter( + Config, 0, <<"federation-upstream">>, <<"localhost">>, + [{<<"uri">>, rabbit_ct_broker_helpers:node_uri(Config, 0)}]), + rabbit_ct_broker_helpers:set_parameter( + Config, 0, + <<"federation-upstream-set">>, <<"upstream">>, + [[{<<"upstream">>, <<"localhost">>}, + {<<"exchange">>, <<"upstream">>}, + {<<"queue">>, <<"upstream">>}], + [{<<"upstream">>, <<"broken-bunny">>}, + {<<"exchange">>, <<"upstream">>}, + {<<"queue">>, <<"upstream">>}]]), + rabbit_ct_broker_helpers:set_policy( + Config, 0, + <<"fed">>, <<"^fed1\.">>, <<"all">>, [{<<"federation-upstream-set">>, <<"upstream">>}]), + rabbit_ct_broker_helpers:set_policy( + Config, 0, + <<"fed">>, <<"^fed1\.">>, <<"all">>, [{<<"federation-upstream-set">>, <<"upstream">>}]), + Config. + +expect(Ch, Q, Fun) when is_function(Fun) -> + amqp_channel:subscribe(Ch, #'basic.consume'{queue = Q, + no_ack = true}, self()), + CTag = receive + #'basic.consume_ok'{consumer_tag = CT} -> CT + end, + Fun(), + amqp_channel:call(Ch, #'basic.cancel'{consumer_tag = CTag}); + +expect(Ch, Q, Payloads) -> + expect(Ch, Q, fun() -> expect(Payloads) end). + +expect(Ch, Q, Payloads, Timeout) -> + expect(Ch, Q, fun() -> expect(Payloads, Timeout) end). + +expect([]) -> + ok; +expect(Payloads) -> + expect(Payloads, 60000). + +expect([], _Timeout) -> + ok; +expect(Payloads, Timeout) -> + receive + {#'basic.deliver'{delivery_tag = DTag}, #amqp_msg{payload = Payload}} -> + case lists:member(Payload, Payloads) of + true -> + ct:pal("Consumed a message: ~tp ~tp left: ~tp", [Payload, DTag, length(Payloads) - 1]), + expect(Payloads -- [Payload], Timeout); + false -> ?assert(false, rabbit_misc:format("received an unexpected payload ~tp", [Payload])) + end + after Timeout -> + ct:fail("Did not receive expected payloads ~tp in time", [Payloads]) + end. + +expect_empty(Ch, Q) -> + ?assertMatch(#'basic.get_empty'{}, + amqp_channel:call(Ch, #'basic.get'{ queue = Q })). + +%%---------------------------------------------------------------------------- +xr(Name) -> rabbit_misc:r(<<"/">>, exchange, Name). + +with_ch(Config, Fun, Methods) -> + Ch = rabbit_ct_client_helpers:open_channel(Config), + declare_all(Config, Ch, Methods), + %% Clean up queues even after test failure. + try + Fun(Ch) + after + delete_all(Ch, Methods), + rabbit_ct_client_helpers:close_channel(Ch) + end, + ok. + +declare_all(Config, Ch, Methods) -> [maybe_declare(Config, Ch, Op) || Op <- Methods]. +delete_all(Ch, Methods) -> + [delete_queue(Ch, Q) || #'queue.declare'{queue = Q} <- Methods]. + +maybe_declare(Config, Ch, #'queue.declare'{} = Method) -> + OneOffCh = rabbit_ct_client_helpers:open_channel(Config), + try + amqp_channel:call(OneOffCh, Method#'queue.declare'{passive = true}) + catch exit:{{shutdown, {server_initiated_close, ?NOT_FOUND, _Message}}, _} -> + amqp_channel:call(Ch, Method) + after + catch rabbit_ct_client_helpers:close_channel(OneOffCh) + end; +maybe_declare(_Config, Ch, #'exchange.declare'{} = Method) -> + amqp_channel:call(Ch, Method). + +delete_queue(Ch, Q) -> + amqp_channel:call(Ch, #'queue.delete'{queue = Q}). + +q(Name) -> + q(Name, []). + +q(Name, undefined) -> + q(Name, []); +q(Name, Args) -> + #'queue.declare'{queue = Name, + durable = true, + arguments = Args}. + +x(Name) -> + x(Name, <<"topic">>). + +x(Name, Type) -> + #'exchange.declare'{exchange = Name, + type = Type, + durable = true}. diff --git a/deps/rabbitmq_exchange_federation/test/restart_federation_link_command_SUITE.erl b/deps/rabbitmq_exchange_federation/test/restart_federation_link_command_SUITE.erl new file mode 100644 index 000000000000..2043c0d17410 --- /dev/null +++ b/deps/rabbitmq_exchange_federation/test/restart_federation_link_command_SUITE.erl @@ -0,0 +1,101 @@ +%% 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(restart_federation_link_command_SUITE). + +-include_lib("amqp_client/include/amqp_client.hrl"). +-include("rabbit_exchange_federation.hrl"). + +-compile(export_all). + +-define(CMD, 'Elixir.RabbitMQ.CLI.Ctl.Commands.RestartFederationLinkCommand'). + +all() -> + [ + {group, federated_down} + ]. + +groups() -> + [ + {federated_down, [], [ + run, + run_not_found, + output + ]} + ]. + +%% ------------------------------------------------------------------- +%% Testsuite setup/teardown. +%% ------------------------------------------------------------------- + +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + Config1 = rabbit_ct_helpers:set_config(Config, [ + {rmq_nodename_suffix, ?MODULE} + ]), + Config2 = rabbit_ct_helpers:run_setup_steps(Config1, + rabbit_ct_broker_helpers:setup_steps() ++ + rabbit_ct_client_helpers:setup_steps()), + Config2. + +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(federated_down, Config) -> + rabbit_federation_test_util:setup_down_federation(Config), + Config; +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. +%% ------------------------------------------------------------------- +run_not_federated(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + {stream, []} = ?CMD:run([], Opts#{'only-down' => false}). + +output_not_federated(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + {stream, []} = ?CMD:output({stream, []}, Opts). + +run(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + rabbit_federation_test_util:with_ch( + Config, + fun(_) -> + timer:sleep(3000), + [Link | _] = rabbit_ct_broker_helpers:rpc(Config, 0, + rabbit_federation_status, status, + []), + Id = proplists:get_value(id, Link), + ok = ?CMD:run([Id], Opts) + end, + [rabbit_federation_test_util:x(<<"fed1.downstream">>)]). + +run_not_found(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + {error, _ErrorMsg} = ?CMD:run([<<"MakingItUp">>], Opts). + +output(Config) -> + [A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename), + Opts = #{node => A}, + ok = ?CMD:output(ok, Opts). diff --git a/deps/rabbitmq_exchange_federation/test/unit_inbroker_SUITE.erl b/deps/rabbitmq_exchange_federation/test/unit_inbroker_SUITE.erl new file mode 100644 index 000000000000..b5da5393e78a --- /dev/null +++ b/deps/rabbitmq_exchange_federation/test/unit_inbroker_SUITE.erl @@ -0,0 +1,110 @@ +%% 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(unit_inbroker_SUITE). + +-include_lib("rabbit_common/include/rabbit.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-compile(export_all). + +-define(US_NAME, <<"upstream">>). +-define(DS_NAME, <<"fed.downstream">>). + +all() -> + [ + {group, non_parallel_tests} + ]. + +groups() -> + [ + {non_parallel_tests, [], [ + serialisation + ]} + ]. + +%% ------------------------------------------------------------------- +%% Testsuite setup/teardown. +%% ------------------------------------------------------------------- + +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + 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. +%% ------------------------------------------------------------------- + +%% Test that we apply binding changes in the correct order even when +%% they arrive out of order. +serialisation(Config) -> + ok = rabbit_ct_broker_helpers:rpc(Config, 0, + ?MODULE, serialisation1, []). + +serialisation1() -> + with_exchanges( + fun(X) -> + [B1, B2, B3] = [b(K) || K <- [<<"1">>, <<"2">>, <<"3">>]], + remove_bindings(4, X, [B1, B3]), + add_binding(5, X, B1), + add_binding(1, X, B1), + add_binding(2, X, B2), + add_binding(3, X, B3), + %% List of lists because one for each link + Keys = rabbit_federation_exchange_link:list_routing_keys( + X#exchange.name), + [[<<"1">>, <<"2">>]] =:= Keys + end). + +with_exchanges(Fun) -> + {ok, _} = rabbit_exchange:declare( + r(?US_NAME), fanout, false, false, false, [], + <<"acting-user">>), + {ok, X} = rabbit_exchange:declare( + r(?DS_NAME), fanout, false, false, false, [], + <<"acting-user">>), + Fun(X), + %% Delete downstream first or it will recreate the upstream + rabbit_exchange:delete(r(?DS_NAME), false, <<"acting-user">>), + rabbit_exchange:delete(r(?US_NAME), false, <<"acting-user">>), + ok. + +add_binding(Ser, X, B) -> + rabbit_federation_exchange:add_binding(transaction, X, B), + rabbit_federation_exchange:add_binding(Ser, X, B). + +remove_bindings(Ser, X, Bs) -> + rabbit_federation_exchange:remove_bindings(transaction, X, Bs), + rabbit_federation_exchange:remove_bindings(Ser, X, Bs). + +r(Name) -> rabbit_misc:r(<<"/">>, exchange, Name). + +b(Key) -> + #binding{source = ?DS_NAME, destination = <<"whatever">>, + key = Key, args = []}. diff --git a/deps/rabbitmq_federation/CODE_OF_CONDUCT.md b/deps/rabbitmq_federation/CODE_OF_CONDUCT.md deleted file mode 120000 index a3613c99f0b0..000000000000 --- a/deps/rabbitmq_federation/CODE_OF_CONDUCT.md +++ /dev/null @@ -1 +0,0 @@ -../../CODE_OF_CONDUCT.md \ No newline at end of file diff --git a/deps/rabbitmq_federation/CONTRIBUTING.md b/deps/rabbitmq_federation/CONTRIBUTING.md deleted file mode 120000 index f939e75f21a8..000000000000 --- a/deps/rabbitmq_federation/CONTRIBUTING.md +++ /dev/null @@ -1 +0,0 @@ -../../CONTRIBUTING.md \ No newline at end of file diff --git a/deps/rabbitmq_federation/LICENSE b/deps/rabbitmq_federation/LICENSE deleted file mode 100644 index e75136bfb5f8..000000000000 --- a/deps/rabbitmq_federation/LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -This package is licensed under the MPL 2.0. For the MPL 2.0, please see LICENSE-MPL-RabbitMQ. - -If you have any questions regarding licensing, please contact us at rabbitmq-core@groups.vmware.com. diff --git a/deps/rabbitmq_federation/LICENSE-MPL-RabbitMQ b/deps/rabbitmq_federation/LICENSE-MPL-RabbitMQ deleted file mode 100644 index 14e2f777f6c3..000000000000 --- a/deps/rabbitmq_federation/LICENSE-MPL-RabbitMQ +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - 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 http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/deps/rabbitmq_federation/Makefile b/deps/rabbitmq_federation/Makefile index 13d055c45d52..bf4504bafa57 100644 --- a/deps/rabbitmq_federation/Makefile +++ b/deps/rabbitmq_federation/Makefile @@ -1,25 +1,11 @@ PROJECT = rabbitmq_federation -PROJECT_DESCRIPTION = RabbitMQ Federation -PROJECT_MOD = rabbit_federation_app +PROJECT_DESCRIPTION = Deprecated no-op RabbitMQ Federation -define PROJECT_ENV -[ - {pgroup_name_cluster_id, false}, - {internal_exchange_check_interval, 90000} - ] -endef +LOCAL_DEPS = rabbit -define PROJECT_APP_EXTRA_KEYS - {broker_version_requirements, []} -endef +DEP_PLUGINS = rabbit_common/mk/rabbitmq-build.mk -DEPS = rabbit_common rabbit amqp_client -TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers - -PLT_APPS += rabbitmq_cli - -DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk -DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk +.DEFAULT_GOAL = all include ../../rabbitmq-components.mk include ../../erlang.mk diff --git a/deps/rabbitmq_federation/README.md b/deps/rabbitmq_federation/README.md index d96c13a02e57..86a5e4bc1fbd 100644 --- a/deps/rabbitmq_federation/README.md +++ b/deps/rabbitmq_federation/README.md @@ -1,23 +1,6 @@ -## RabbitMQ Federation +This no-op plugin exists only such that deployment tools can continue to enable and disable this plugin without erroring: -RabbitMQ federation offers a group of features for loosely -coupled and WAN-friendly distributed RabbitMQ setups. Note that -this is not an alternative to queue mirroring. - - -## Supported RabbitMQ Versions - -This plugin ships with RabbitMQ, there is no need to -install it separately. - - -## Documentation - -See [RabbitMQ federation plugin](https://www.rabbitmq.com/federation.html) on rabbitmq.com. - - -## License and Copyright - -Released under [the same license as RabbitMQ](https://www.rabbitmq.com/mpl.html). - -2007-2015 (c) 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +``` +rabbitmq-plugins enable rabbitmq_federation +rabbitmq-plugins disable rabbitmq_federation +``` diff --git a/deps/rabbitmq_federation/include/logging.hrl b/deps/rabbitmq_federation/include/logging.hrl deleted file mode 100644 index 019713e11b45..000000000000 --- a/deps/rabbitmq_federation/include/logging.hrl +++ /dev/null @@ -1,3 +0,0 @@ --include_lib("rabbit_common/include/logging.hrl"). - --define(RMQLOG_DOMAIN_FEDERATION, ?DEFINE_RMQLOG_DOMAIN(federation)). diff --git a/deps/rabbitmq_federation/src/rabbitmq_federation_noop.erl b/deps/rabbitmq_federation/src/rabbitmq_federation_noop.erl new file mode 100644 index 000000000000..708d7b84fe9b --- /dev/null +++ b/deps/rabbitmq_federation/src/rabbitmq_federation_noop.erl @@ -0,0 +1 @@ +-module(rabbitmq_federation_noop). diff --git a/deps/rabbitmq_federation_common/Makefile b/deps/rabbitmq_federation_common/Makefile new file mode 100644 index 000000000000..beab43bb81ff --- /dev/null +++ b/deps/rabbitmq_federation_common/Makefile @@ -0,0 +1,25 @@ +PROJECT = rabbitmq_federation_common +PROJECT_DESCRIPTION = RabbitMQ Federation Common +PROJECT_MOD = rabbit_federation_common_app + +define PROJECT_ENV +[ + {pgroup_name_cluster_id, false}, + {internal_exchange_check_interval, 90000} + ] +endef + +define PROJECT_APP_EXTRA_KEYS + {broker_version_requirements, []} +endef + +DEPS = rabbit_common rabbit amqp_client +TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers + +PLT_APPS += rabbitmq_cli + +DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk +DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk + +include ../../rabbitmq-components.mk +include ../../erlang.mk diff --git a/deps/rabbitmq_federation_common/include/logging.hrl b/deps/rabbitmq_federation_common/include/logging.hrl new file mode 100644 index 000000000000..20ad0459af58 --- /dev/null +++ b/deps/rabbitmq_federation_common/include/logging.hrl @@ -0,0 +1,3 @@ +-include_lib("rabbit_common/include/logging.hrl"). + +-define(RMQLOG_DOMAIN_FEDERATION, ?DEFINE_RMQLOG_DOMAIN(queue_federation)). diff --git a/deps/rabbitmq_federation/include/rabbit_federation.hrl b/deps/rabbitmq_federation_common/include/rabbit_federation.hrl similarity index 96% rename from deps/rabbitmq_federation/include/rabbit_federation.hrl rename to deps/rabbitmq_federation_common/include/rabbit_federation.hrl index e5be82ef4969..96361e516f8d 100644 --- a/deps/rabbitmq_federation/include/rabbit_federation.hrl +++ b/deps/rabbitmq_federation_common/include/rabbit_federation.hrl @@ -45,4 +45,4 @@ -define(FEDERATION_GUIDE_URL, <<"https://rabbitmq.com/docs/federation/">>). --define(FEDERATION_PG_SCOPE, rabbitmq_federation_pg_scope). +-define(FEDERATION_ETS, rabbit_federation_common). diff --git a/deps/rabbitmq_federation/src/Elixir.RabbitMQ.CLI.Ctl.Commands.FederationStatusCommand.erl b/deps/rabbitmq_federation_common/src/Elixir.RabbitMQ.CLI.Ctl.Commands.FederationStatusCommand.erl similarity index 100% rename from deps/rabbitmq_federation/src/Elixir.RabbitMQ.CLI.Ctl.Commands.FederationStatusCommand.erl rename to deps/rabbitmq_federation_common/src/Elixir.RabbitMQ.CLI.Ctl.Commands.FederationStatusCommand.erl diff --git a/deps/rabbitmq_federation/src/Elixir.RabbitMQ.CLI.Ctl.Commands.RestartFederationLinkCommand.erl b/deps/rabbitmq_federation_common/src/Elixir.RabbitMQ.CLI.Ctl.Commands.RestartFederationLinkCommand.erl similarity index 100% rename from deps/rabbitmq_federation/src/Elixir.RabbitMQ.CLI.Ctl.Commands.RestartFederationLinkCommand.erl rename to deps/rabbitmq_federation_common/src/Elixir.RabbitMQ.CLI.Ctl.Commands.RestartFederationLinkCommand.erl diff --git a/deps/rabbitmq_federation_common/src/rabbit_federation_common_app.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_common_app.erl new file mode 100644 index 000000000000..88700f8fd1e2 --- /dev/null +++ b/deps/rabbitmq_federation_common/src/rabbit_federation_common_app.erl @@ -0,0 +1,33 @@ +%% 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_federation_common_app). + +-include("rabbit_federation.hrl"). + +-behaviour(application). +-export([start/2, stop/1]). + +-behaviour(supervisor). +-export([init/1]). + +start(_Type, _StartArgs) -> + ?FEDERATION_ETS = ets:new(?FEDERATION_ETS, [set, public, named_table]), + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +stop(_State) -> + ok. + +%%---------------------------------------------------------------------------- + +init([]) -> + Flags = #{ + strategy => one_for_one, + intensity => 3, + period => 10 + }, + {ok, {Flags, []}}. diff --git a/deps/rabbitmq_federation/src/rabbit_federation_db.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_db.erl similarity index 100% rename from deps/rabbitmq_federation/src/rabbit_federation_db.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_db.erl diff --git a/deps/rabbitmq_federation/src/rabbit_federation_event.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_event.erl similarity index 100% rename from deps/rabbitmq_federation/src/rabbit_federation_event.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_event.erl diff --git a/deps/rabbitmq_federation/src/rabbit_federation_link_sup.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_link_sup.erl similarity index 67% rename from deps/rabbitmq_federation/src/rabbit_federation_link_sup.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_link_sup.erl index e52c0c889cf0..7c76aafbd994 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_link_sup.erl +++ b/deps/rabbitmq_federation_common/src/rabbit_federation_link_sup.erl @@ -15,18 +15,19 @@ %% Supervises the upstream links for an exchange or queue. --export([start_link/1, adjust/3, restart/2]). +-export([start_link/2, adjust/4, restart/2]). -export([init/1]). -start_link(XorQ) -> - supervisor2:start_link(?MODULE, XorQ). +start_link(LinkMod, Q) -> + supervisor2:start_link(?MODULE, [LinkMod, Q]). -adjust(Sup, XorQ, everything) -> +adjust(Sup, LinkMod, XorQ, everything) -> _ = [stop(Sup, Upstream, XorQ) || {Upstream, _, _, _} <- supervisor2:which_children(Sup)], - [{ok, _Pid} = supervisor2:start_child(Sup, Spec) || Spec <- specs(XorQ)]; + [{ok, _Pid} = supervisor2:start_child(Sup, Spec) + || Spec <- specs(LinkMod, XorQ)]; -adjust(Sup, XorQ, {upstream, UpstreamName}) -> +adjust(Sup, LinkMod, XorQ, {upstream, UpstreamName}) -> OldUpstreams0 = children(Sup, UpstreamName), NewUpstreams0 = rabbit_federation_upstream:for(XorQ, UpstreamName), %% If any haven't changed, don't restart them. The broker will @@ -42,32 +43,32 @@ adjust(Sup, XorQ, {upstream, UpstreamName}) -> end end, {OldUpstreams0, NewUpstreams0}, OldUpstreams0), _ = [stop(Sup, OldUpstream, XorQ) || OldUpstream <- OldUpstreams], - [start(Sup, NewUpstream, XorQ) || NewUpstream <- NewUpstreams]; + [start(Sup, LinkMod, NewUpstream, XorQ) || NewUpstream <- NewUpstreams]; -adjust(Sup, XorQ, {clear_upstream, UpstreamName}) -> +adjust(Sup, _LinkMod, XorQ, {clear_upstream, UpstreamName}) -> ok = rabbit_federation_db:prune_scratch( name(XorQ), rabbit_federation_upstream:for(XorQ)), [stop(Sup, Upstream, XorQ) || Upstream <- children(Sup, UpstreamName)]; -adjust(Sup, X = #exchange{name = XName}, {upstream_set, _Set}) -> - _ = adjust(Sup, X, everything), +adjust(Sup, LinkMod, X = #exchange{name = XName}, {upstream_set, _Set}) -> + _ = adjust(Sup, LinkMod, X, everything), case rabbit_federation_upstream:federate(X) of false -> ok; true -> ok = rabbit_federation_db:prune_scratch( XName, rabbit_federation_upstream:for(X)) end; -adjust(Sup, Q, {upstream_set, _}) when ?is_amqqueue(Q) -> - adjust(Sup, Q, everything); -adjust(Sup, XorQ, {clear_upstream_set, _}) -> - adjust(Sup, XorQ, everything). +adjust(Sup, LinkMod, Q, {upstream_set, _}) when ?is_amqqueue(Q) -> + adjust(Sup, LinkMod, Q, everything); +adjust(Sup, LinkMod, XorQ, {clear_upstream_set, _}) -> + adjust(Sup, LinkMod, XorQ, everything). restart(Sup, Upstream) -> ok = supervisor2:terminate_child(Sup, Upstream), {ok, _Pid} = supervisor2:restart_child(Sup, Upstream), ok. -start(Sup, Upstream, XorQ) -> - {ok, _Pid} = supervisor2:start_child(Sup, spec(rabbit_federation_util:obfuscate_upstream(Upstream), XorQ)), +start(Sup, LinkMod, Upstream, XorQ) -> + {ok, _Pid} = supervisor2:start_child(Sup, spec(LinkMod, rabbit_federation_util:obfuscate_upstream(Upstream), XorQ)), ok. stop(Sup, Upstream, XorQ) -> @@ -78,6 +79,7 @@ stop(Sup, Upstream, XorQ) -> %% come up, the possibility exists that there *is* no link %% process, but we still have a report in the status table. So %% remove it here too. + %% TODO how do we figure out the module without adding a dependency? rabbit_federation_status:remove(Upstream, name(XorQ)). children(Sup, UpstreamName) -> @@ -86,24 +88,24 @@ children(Sup, UpstreamName) -> %%---------------------------------------------------------------------------- -init(XorQ) -> +init([LinkMod, XorQ]) -> %% 1, ?MAX_WAIT so that we always give up after one fast retry and get %% into the reconnect delay. - {ok, {{one_for_one, 1, ?MAX_WAIT}, specs(XorQ)}}. + {ok, {{one_for_one, 1, ?MAX_WAIT}, specs(LinkMod, XorQ)}}. -specs(XorQ) -> - [spec(rabbit_federation_util:obfuscate_upstream(Upstream), XorQ) +specs(LinkMod, XorQ) -> + [spec(LinkMod, rabbit_federation_util:obfuscate_upstream(Upstream), XorQ) || Upstream <- rabbit_federation_upstream:for(XorQ)]. -spec(U = #upstream{reconnect_delay = Delay}, #exchange{name = XName}) -> - {U, {rabbit_federation_exchange_link, start_link, [{U, XName}]}, +spec(LinkMod, U = #upstream{reconnect_delay = Delay}, #exchange{name = XName}) -> + {U, {LinkMod, start_link, [{U, XName}]}, {permanent, Delay}, ?WORKER_WAIT, worker, - [rabbit_federation_exchange_link]}; + [LinkMod]}; -spec(Upstream = #upstream{reconnect_delay = Delay}, Q) when ?is_amqqueue(Q) -> - {Upstream, {rabbit_federation_queue_link, start_link, [{Upstream, Q}]}, +spec(LinkMod, Upstream = #upstream{reconnect_delay = Delay}, Q) when ?is_amqqueue(Q) -> + {Upstream, {LinkMod, start_link, [{Upstream, Q}]}, {permanent, Delay}, ?WORKER_WAIT, worker, - [rabbit_federation_queue_link]}. + [LinkMod]}. name(#exchange{name = XName}) -> XName; name(Q) when ?is_amqqueue(Q) -> amqqueue:get_name(Q). diff --git a/deps/rabbitmq_federation/src/rabbit_federation_link_util.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_link_util.erl similarity index 99% rename from deps/rabbitmq_federation/src/rabbit_federation_link_util.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_link_util.erl index bbedc954babf..16c87d2cc9c7 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_link_util.erl +++ b/deps/rabbitmq_federation_common/src/rabbit_federation_link_util.erl @@ -263,10 +263,10 @@ handle_downstream_down(Reason, _Args, State) -> %% If the upstream channel goes down for an intelligible reason, just %% log it and die quietly. handle_upstream_down(shutdown, {Upstream, UParams, XName}, State) -> - rabbit_federation_link_util:connection_error( + connection_error( remote, {upstream_channel_down, shutdown}, Upstream, UParams, XName, State); handle_upstream_down({shutdown, Reason}, {Upstream, UParams, XName}, State) -> - rabbit_federation_link_util:connection_error( + connection_error( remote, {upstream_channel_down, Reason}, Upstream, UParams, XName, State); handle_upstream_down(Reason, _Args, State) -> diff --git a/deps/rabbitmq_federation/src/rabbit_federation_parameters.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_parameters.erl similarity index 95% rename from deps/rabbitmq_federation/src/rabbit_federation_parameters.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_parameters.erl index 02c498d69dd5..b364a6849ac5 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_parameters.erl +++ b/deps/rabbitmq_federation_common/src/rabbit_federation_parameters.erl @@ -9,6 +9,8 @@ -behaviour(rabbit_runtime_parameter). -behaviour(rabbit_policy_validator). +-include("rabbit_federation.hrl"). + -export([validate/5, notify/5, notify_clear/4]). -export([register/0, unregister/0, validate_policy/1, adjust/1]). @@ -64,12 +66,12 @@ notify_clear(_VHost, <<"federation-upstream-set">>, Name, _Username) -> adjust({clear_upstream_set, Name}); notify_clear(VHost, <<"federation-upstream">>, Name, _Username) -> - rabbit_federation_exchange_link_sup_sup:adjust({clear_upstream, VHost, Name}), - rabbit_federation_queue_link_sup_sup:adjust({clear_upstream, VHost, Name}). + adjust({clear_upstream, VHost, Name}). adjust(Thing) -> - rabbit_federation_exchange_link_sup_sup:adjust(Thing), - rabbit_federation_queue_link_sup_sup:adjust(Thing). + Plugins = ets:tab2list(?FEDERATION_ETS), + _ = [Module:adjust(Thing) || {_Name, #{link_module := Module}} <- Plugins], + ok. %%---------------------------------------------------------------------------- diff --git a/deps/rabbitmq_federation/src/rabbit_federation_pg.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_pg.erl similarity index 65% rename from deps/rabbitmq_federation/src/rabbit_federation_pg.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_pg.erl index fceb7b54217f..2f3ee5f24464 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_pg.erl +++ b/deps/rabbitmq_federation_common/src/rabbit_federation_pg.erl @@ -7,18 +7,16 @@ -module(rabbit_federation_pg). --include("rabbit_federation.hrl"). +-export([start_scope/1, stop_scope/1]). --export([start_scope/0, stop_scope/0]). +start_scope(Scope) -> + rabbit_log_federation:debug("Starting pg scope ~ts", [Scope]), + _ = pg:start_link(Scope). -start_scope() -> - rabbit_log_federation:debug("Starting pg scope ~ts", [?FEDERATION_PG_SCOPE]), - _ = pg:start_link(?FEDERATION_PG_SCOPE). - -stop_scope() -> - case whereis(?FEDERATION_PG_SCOPE) of +stop_scope(Scope) -> + case whereis(Scope) of Pid when is_pid(Pid) -> - rabbit_log_federation:debug("Stopping pg scope ~ts", [?FEDERATION_PG_SCOPE]), + rabbit_log_federation:debug("Stopping pg scope ~ts", [Scope]), exit(Pid, normal); _ -> ok diff --git a/deps/rabbitmq_federation/src/rabbit_federation_status.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_status.erl similarity index 100% rename from deps/rabbitmq_federation/src/rabbit_federation_status.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_status.erl diff --git a/deps/rabbitmq_federation/src/rabbit_federation_sup.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_sup.erl similarity index 70% rename from deps/rabbitmq_federation/src/rabbit_federation_sup.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_sup.erl index 5956d6a7c87e..bbe0f71badab 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_sup.erl +++ b/deps/rabbitmq_federation_common/src/rabbit_federation_sup.erl @@ -12,8 +12,9 @@ %% Supervises everything. There is just one of these. -include_lib("rabbit_common/include/rabbit.hrl"). +-include("rabbit_federation.hrl"). --define(SUPERVISOR, rabbit_federation_sup). +-define(SUPERVISOR, ?MODULE). -export([start_link/0, stop/0]). @@ -27,9 +28,7 @@ [{description, "federation"}, {mfa, {rabbit_sup, start_child, [?MODULE]}}, {requires, kernel_ready}, - {cleanup, {?MODULE, stop, []}}, - {enables, rabbit_federation_exchange}, - {enables, rabbit_federation_queue}]}). + {cleanup, {?MODULE, stop, []}}]}). %%---------------------------------------------------------------------------- @@ -54,22 +53,6 @@ init([]) -> type => worker, modules => [rabbit_federation_status] }, - XLinkSupSup = #{ - id => x_links, - start => {rabbit_federation_exchange_link_sup_sup, start_link, []}, - restart => transient, - shutdown => ?SUPERVISOR_WAIT, - type => supervisor, - modules =>[rabbit_federation_exchange_link_sup_sup] - }, - QLinkSupSup = #{ - id => q_links, - start => {rabbit_federation_queue_link_sup_sup, start_link, []}, - restart => transient, - shutdown => ?SUPERVISOR_WAIT, - type => supervisor, - modules => [rabbit_federation_queue_link_sup_sup] - }, %% with default reconnect-delay of 5 second, this supports up to %% 100 links constantly failing and being restarted a minute %% (or 200 links if reconnect-delay is 10 seconds, 600 with 30 seconds, @@ -79,5 +62,5 @@ init([]) -> intensity => 1200, period => 60 }, - Specs = [Status, XLinkSupSup, QLinkSupSup], + Specs = [Status], {ok, {Flags, Specs}}. diff --git a/deps/rabbitmq_federation/src/rabbit_federation_upstream.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_upstream.erl similarity index 100% rename from deps/rabbitmq_federation/src/rabbit_federation_upstream.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_upstream.erl diff --git a/deps/rabbitmq_federation/src/rabbit_federation_util.erl b/deps/rabbitmq_federation_common/src/rabbit_federation_util.erl similarity index 100% rename from deps/rabbitmq_federation/src/rabbit_federation_util.erl rename to deps/rabbitmq_federation_common/src/rabbit_federation_util.erl diff --git a/deps/rabbitmq_federation/src/rabbit_log_federation.erl b/deps/rabbitmq_federation_common/src/rabbit_log_federation.erl similarity index 100% rename from deps/rabbitmq_federation/src/rabbit_log_federation.erl rename to deps/rabbitmq_federation_common/src/rabbit_log_federation.erl diff --git a/deps/rabbitmq_federation_common/test/definition_import_SUITE_data/case1.json b/deps/rabbitmq_federation_common/test/definition_import_SUITE_data/case1.json new file mode 100644 index 000000000000..e549e4fd6c1d --- /dev/null +++ b/deps/rabbitmq_federation_common/test/definition_import_SUITE_data/case1.json @@ -0,0 +1,52 @@ +{ + "permissions": [ + { + "configure": ".*", + "read": ".*", + "user": "guest", + "vhost": "/", + "write": ".*" + } + ], + "bindings": [], + "queues": [], + "parameters": [ + { + "component": "federation-upstream-set", + "name": "location-1", + "value": [ + { + "upstream":"up-1" + }, + { + "upstream":"up-2" + } + ], + "vhost":"/"}], + "policies": [], + "rabbitmq_version": "3.13.0+376.g1bc0d89.dirty", + "users": [ + { + "hashing_algorithm": "rabbit_password_hashing_sha256", + "limits": {}, + "name": "guest", + "password_hash": "jTcCKuOmGJeeRQ/K1LG5sdZLcdnEnqv8wcrP2n68R7nMuqy2", + "tags": ["administrator"] + } + ], + "rabbit_version": "3.13.0+376.g1bc0d89.dirty", + "exchanges": [], + "topic_permissions": [], + "vhosts": [ + { + "limits": [], + "metadata": + { + "description": "Default virtual host", + "tags": [] + }, + "name":"/" + } + ], + "global_parameters": [] +} diff --git a/deps/rabbitmq_federation/test/unit_SUITE.erl b/deps/rabbitmq_federation_common/test/unit_SUITE.erl similarity index 100% rename from deps/rabbitmq_federation/test/unit_SUITE.erl rename to deps/rabbitmq_federation_common/test/unit_SUITE.erl diff --git a/deps/rabbitmq_federation/test/unit_inbroker_SUITE.erl b/deps/rabbitmq_federation_common/test/unit_inbroker_SUITE.erl similarity index 87% rename from deps/rabbitmq_federation/test/unit_inbroker_SUITE.erl rename to deps/rabbitmq_federation_common/test/unit_inbroker_SUITE.erl index 83b44579c74f..eb40a1d16762 100644 --- a/deps/rabbitmq_federation/test/unit_inbroker_SUITE.erl +++ b/deps/rabbitmq_federation_common/test/unit_inbroker_SUITE.erl @@ -25,7 +25,6 @@ all() -> groups() -> [ {non_parallel_tests, [], [ - serialisation, scratch_space, remove_credentials, get_connection_name, @@ -68,27 +67,6 @@ end_per_testcase(Testcase, Config) -> %% Testcases. %% ------------------------------------------------------------------- -%% Test that we apply binding changes in the correct order even when -%% they arrive out of order. -serialisation(Config) -> - ok = rabbit_ct_broker_helpers:rpc(Config, 0, - ?MODULE, serialisation1, []). - -serialisation1() -> - with_exchanges( - fun(X) -> - [B1, B2, B3] = [b(K) || K <- [<<"1">>, <<"2">>, <<"3">>]], - remove_bindings(4, X, [B1, B3]), - add_binding(5, X, B1), - add_binding(1, X, B1), - add_binding(2, X, B2), - add_binding(3, X, B3), - %% List of lists because one for each link - Keys = rabbit_federation_exchange_link:list_routing_keys( - X#exchange.name), - [[<<"1">>, <<"2">>]] =:= Keys - end). - scratch_space(Config) -> ok = rabbit_ct_broker_helpers:rpc(Config, 0, ?MODULE, scratch_space1, []). @@ -212,14 +190,6 @@ with_exchanges(Fun) -> rabbit_exchange:delete(r(?US_NAME), false, <<"acting-user">>), ok. -add_binding(Ser, X, B) -> - rabbit_federation_exchange:add_binding(transaction, X, B), - rabbit_federation_exchange:add_binding(Ser, X, B). - -remove_bindings(Ser, X, Bs) -> - rabbit_federation_exchange:remove_bindings(transaction, X, Bs), - rabbit_federation_exchange:remove_bindings(Ser, X, Bs). - r(Name) -> rabbit_misc:r(<<"/">>, exchange, Name). b(Key) -> diff --git a/deps/rabbitmq_federation_management/Makefile b/deps/rabbitmq_federation_management/Makefile index 71ecae0fa504..960be5f55366 100644 --- a/deps/rabbitmq_federation_management/Makefile +++ b/deps/rabbitmq_federation_management/Makefile @@ -5,8 +5,8 @@ define PROJECT_APP_EXTRA_KEYS {broker_version_requirements, []} endef -DEPS = amqp_client rabbit_common rabbit rabbitmq_management rabbitmq_federation -TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers +DEPS = amqp_client rabbit_common rabbit rabbitmq_management rabbitmq_federation_common +TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers rabbitmq_queue_federation rabbitmq_exchange_federation DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk diff --git a/deps/rabbitmq_federation_prometheus/Makefile b/deps/rabbitmq_federation_prometheus/Makefile index 81e2b259b7b4..c7955f4b5e5b 100644 --- a/deps/rabbitmq_federation_prometheus/Makefile +++ b/deps/rabbitmq_federation_prometheus/Makefile @@ -6,8 +6,8 @@ define PROJECT_APP_EXTRA_KEYS {broker_version_requirements, []} endef -DEPS = rabbit_common rabbit rabbitmq_federation rabbitmq_prometheus -TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers +DEPS = rabbit_common rabbit rabbitmq_federation_common rabbitmq_prometheus +TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers rabbitmq_queue_federation rabbitmq_exchange_federation DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk diff --git a/deps/rabbitmq_mqtt/Makefile b/deps/rabbitmq_mqtt/Makefile index fde095031a52..27fc5846ccd9 100644 --- a/deps/rabbitmq_mqtt/Makefile +++ b/deps/rabbitmq_mqtt/Makefile @@ -43,7 +43,7 @@ export BUILD_WITHOUT_QUIC LOCAL_DEPS = ssl DEPS = ranch rabbit amqp10_common -TEST_DEPS = cowlib emqtt ct_helper rabbitmq_ct_helpers rabbitmq_ct_client_helpers rabbitmq_management amqp_client rabbitmq_consistent_hash_exchange rabbitmq_amqp_client rabbitmq_stomp rabbitmq_stream rabbitmq_federation +TEST_DEPS = cowlib emqtt ct_helper rabbitmq_ct_helpers rabbitmq_ct_client_helpers rabbitmq_management amqp_client rabbitmq_consistent_hash_exchange rabbitmq_amqp_client rabbitmq_stomp rabbitmq_stream rabbitmq_exchange_federation PLT_APPS += rabbitmq_cli elixir diff --git a/deps/rabbitmq_queue_federation/Makefile b/deps/rabbitmq_queue_federation/Makefile new file mode 100644 index 000000000000..1e30fe9d7c9e --- /dev/null +++ b/deps/rabbitmq_queue_federation/Makefile @@ -0,0 +1,24 @@ +PROJECT = rabbitmq_queue_federation +PROJECT_DESCRIPTION = RabbitMQ Queue Federation +PROJECT_MOD = rabbit_queue_federation_app + +define PROJECT_ENV +[ + {pgroup_name_cluster_id, false} + ] +endef + +define PROJECT_APP_EXTRA_KEYS + {broker_version_requirements, []} +endef + +DEPS = rabbit_common rabbit amqp_client rabbitmq_federation_common +TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers + +PLT_APPS += rabbitmq_cli + +DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk +DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk + +include ../../rabbitmq-components.mk +include ../../erlang.mk diff --git a/deps/rabbitmq_queue_federation/README-hacking b/deps/rabbitmq_queue_federation/README-hacking new file mode 100644 index 000000000000..6432552fe33a --- /dev/null +++ b/deps/rabbitmq_queue_federation/README-hacking @@ -0,0 +1,143 @@ +This file is intended to tell you How It All Works, concentrating on +the things you might not expect. + +The theory +========== + +The 'x-federation' exchange is defined in +rabbit_federation_exchange. This starts up a bunch of link processes +(one for each upstream) which: + + * Connect to the upstream broker + * Create a queue and bind it to the upstream exchange + * Keep bindings in sync with the downstream exchange + * Consume messages from the upstream queue and republish them to the + downstream exchange (matching confirms with acks) + +Each link process monitors the connections / channels it opens, and +dies if they do. We use a supervisor2 to ensure that we get some +backoff when restarting. + +We use process groups to identify all link processes for a certain +exchange, as well as all link processes together. + +However, there are a bunch of wrinkles: + + +Wrinkle: The exchange will be recovered when the Erlang client is not available +=============================================================================== + +Exchange recovery happens within the rabbit application - therefore at +the time that the exchange is recovered, we can't make any connections +since the amqp_client application has not yet started. Each link +therefore initially has a state 'not_started'. When it is created it +checks to see if the rabbitmq_federation application is running. If +so, it starts fully. If not, it goes into the 'not_started' +state. When rabbitmq_federation starts, it sends a 'go' message to all +links, prodding them to bring up the link. + + +Wrinkle: On reconnect we want to assert bindings atomically +=========================================================== + +If the link goes down for whatever reason, then by the time it comes +up again the bindings downstream may no longer be in sync with those +upstream. Therefore on link establishment we want to ensure that a +certain set of bindings exists. (Of course bringing up a link for the +first time is a simple case of this.) And we want to do this with AMQP +methods. But if we were to tear down all bindings and recreate them, +we would have a time period when messages would not be forwarded for +bindings that *do* still exist before and after. + +We use exchange to exchange bindings to work around this: + +We bind the upstream exchange (X) to the upstream queue (Q) via an +internal fanout exchange (IXA) like so: (routing keys R1 and R2): + + X----R1,R2--->IXA---->Q + +This has the same effect as binding the queue to the exchange directly. + +Now imagine the link has gone down, and is about to be +reestablished. In the meanwhile, routing has changed downstream so +that we now want routing keys R1 and R3. On link reconnection we can +create and bind another internal fanout exchange IXB: + + X----R1,R2--->IXA---->Q + | ^ + | | + \----R1,R3--->IXB-----/ + +and then delete the original exchange IXA: + + X Q + | ^ + | | + \----R1,R3--->IXB-----/ + +This means that messages matching R1 are always routed during the +switchover. Messages for R3 will start being routed as soon as we bind +the second exchange, and messages for R2 will be stopped in a timely +way. Of course this could lag the downstream situation somewhat, in +which case some R2 messages will get thrown away downstream since they +are unroutable. However this lag is inevitable when the link goes +down. + +This means that the downstream only needs to keep track of whether the +upstream is currently going via internal exchange A or B. This is +held in the exchange scratch space in Mnesia. + + +Wrinkle: We need to amalgamate bindings +======================================= + +Since we only bind to one exchange upstream, but the downstream +exchange can be bound to many queues, we can have duplicated bindings +downstream (same source, routing key and args but different +destination) that cannot be duplicated upstream (since the destination +is the same). The link therefore maintains a mapping of (Key, Args) to +set(Dest). Duplicated bindings do not get repeated upstream, and are +only unbound upstream when the last one goes away downstream. + +Furthermore, this works as an optimisation since this will tend to +reduce upstream binding count and churn. + + +Wrinkle: We may receive binding events out of order +=================================================== + +The rabbit_federation_exchange callbacks are invoked by channel +processes within rabbit. Therefore they can be executed concurrently, +and can arrive at the link processes in an order that does not +correspond to the wall clock. + +We need to keep the state of the link in sync with Mnesia. Therefore +not only do we need to impose an ordering on these events, we need to +impose Mnesia's ordering on them. We therefore added a function to the +callback interface, serialise_events. When this returns true, the +callback mechanism inside rabbit increments a per-exchange counter +within an Mnesia transaction, and returns the value as part of the +add_binding and remove_binding callbacks. The link process then queues +up these events, and replays them in order. The link process's state +thus always follows Mnesia (it may be delayed, but the effects happen +in the same order). + + +Other issues +============ + +Since links are implemented in terms of AMQP, link failure may cause +messages to be redelivered. If you're unlucky this could lead to +duplication. + +Message duplication can also happen with some topologies. In some +cases it may not be possible to set max_hops such that messages arrive +once at every node. + +While we correctly order bind / unbind events, we don't do the same +thing for exchange creation / deletion. (This is harder - if you +delete and recreate an exchange with the same name, is it the same +exchange? What about if its type changes?) This would only be an issue +if exchanges churn rapidly; however we could get into a state where +Mnesia sees CDCD but we see CDDC and leave a process running when we +shouldn't. diff --git a/deps/rabbitmq_queue_federation/README.md b/deps/rabbitmq_queue_federation/README.md new file mode 100644 index 000000000000..d96c13a02e57 --- /dev/null +++ b/deps/rabbitmq_queue_federation/README.md @@ -0,0 +1,23 @@ +## RabbitMQ Federation + +RabbitMQ federation offers a group of features for loosely +coupled and WAN-friendly distributed RabbitMQ setups. Note that +this is not an alternative to queue mirroring. + + +## Supported RabbitMQ Versions + +This plugin ships with RabbitMQ, there is no need to +install it separately. + + +## Documentation + +See [RabbitMQ federation plugin](https://www.rabbitmq.com/federation.html) on rabbitmq.com. + + +## License and Copyright + +Released under [the same license as RabbitMQ](https://www.rabbitmq.com/mpl.html). + +2007-2015 (c) 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. diff --git a/deps/rabbitmq_queue_federation/include/rabbit_queue_federation.hrl b/deps/rabbitmq_queue_federation/include/rabbit_queue_federation.hrl new file mode 100644 index 000000000000..9b9ae71aa9ee --- /dev/null +++ b/deps/rabbitmq_queue_federation/include/rabbit_queue_federation.hrl @@ -0,0 +1,8 @@ +%% 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. +%% + +-define(FEDERATION_PG_SCOPE, rabbitmq_queue_federation_pg_scope). diff --git a/deps/rabbitmq_federation/src/rabbit_federation_queue.erl b/deps/rabbitmq_queue_federation/src/rabbit_federation_queue.erl similarity index 98% rename from deps/rabbitmq_federation/src/rabbit_federation_queue.erl rename to deps/rabbitmq_queue_federation/src/rabbit_federation_queue.erl index 422d8fc39734..b4923f5b283c 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_queue.erl +++ b/deps/rabbitmq_queue_federation/src/rabbit_federation_queue.erl @@ -17,7 +17,7 @@ {enables, recovery}]}). -include_lib("rabbit/include/amqqueue.hrl"). --include("rabbit_federation.hrl"). +-include("rabbit_queue_federation.hrl"). -behaviour(rabbit_queue_decorator). diff --git a/deps/rabbitmq_federation/src/rabbit_federation_queue_link.erl b/deps/rabbitmq_queue_federation/src/rabbit_federation_queue_link.erl similarity index 98% rename from deps/rabbitmq_federation/src/rabbit_federation_queue_link.erl rename to deps/rabbitmq_queue_federation/src/rabbit_federation_queue_link.erl index 9bfa8faf91c4..fda313f63db6 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_queue_link.erl +++ b/deps/rabbitmq_queue_federation/src/rabbit_federation_queue_link.erl @@ -9,7 +9,8 @@ -include_lib("rabbit/include/amqqueue.hrl"). -include_lib("amqp_client/include/amqp_client.hrl"). --include("rabbit_federation.hrl"). +-include_lib("rabbitmq_federation_common/include/rabbit_federation.hrl"). +-include("rabbit_queue_federation.hrl"). -behaviour(gen_server2). @@ -31,7 +32,7 @@ start_link(Args) -> run(QName) -> cast(QName, run). pause(QName) -> cast(QName, pause). go() -> - _ = rabbit_federation_pg:start_scope(), + _ = rabbit_federation_pg:start_scope(?FEDERATION_PG_SCOPE), cast(go). %%---------------------------------------------------------------------------- diff --git a/deps/rabbitmq_federation/src/rabbit_federation_queue_link_sup_sup.erl b/deps/rabbitmq_queue_federation/src/rabbit_federation_queue_link_sup_sup.erl similarity index 90% rename from deps/rabbitmq_federation/src/rabbit_federation_queue_link_sup_sup.erl rename to deps/rabbitmq_queue_federation/src/rabbit_federation_queue_link_sup_sup.erl index 108e4cb0f93b..945c5d35cc85 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_queue_link_sup_sup.erl +++ b/deps/rabbitmq_queue_federation/src/rabbit_federation_queue_link_sup_sup.erl @@ -11,6 +11,7 @@ -include_lib("rabbit_common/include/rabbit.hrl"). -include_lib("rabbit/include/amqqueue.hrl"). +-include("rabbit_queue_federation.hrl"). -define(SUPERVISOR, ?MODULE). %% Supervises the upstream links for all queues (but not exchanges). We need @@ -27,7 +28,7 @@ start_link() -> %% This scope is used by concurrently starting exchange and queue links, %% and other places, so we have to start it very early outside of the supervision tree. %% The scope is stopped in stop/1. - _ = rabbit_federation_pg:start_scope(), + _ = rabbit_federation_pg:start_scope(?FEDERATION_PG_SCOPE), mirrored_supervisor:start_link({local, ?SUPERVISOR}, ?SUPERVISOR, ?MODULE, []). @@ -36,7 +37,7 @@ start_link() -> start_child(Q) -> case mirrored_supervisor:start_child( ?SUPERVISOR, - {id(Q), {rabbit_federation_link_sup, start_link, [Q]}, + {id(Q), {rabbit_federation_link_sup, start_link, [rabbit_federation_queue_link, Q]}, transient, ?SUPERVISOR_WAIT, supervisor, [rabbit_federation_link_sup]}) of {ok, _Pid} -> ok; @@ -51,12 +52,12 @@ start_child(Q) -> adjust({clear_upstream, VHost, UpstreamName}) -> - _ = [rabbit_federation_link_sup:adjust(Pid, Q, {clear_upstream, UpstreamName}) || + _ = [rabbit_federation_link_sup:adjust(Pid, rabbit_federation_queue_link, Q, {clear_upstream, UpstreamName}) || {Q, Pid, _, _} <- mirrored_supervisor:which_children(?SUPERVISOR), ?amqqueue_vhost_equals(Q, VHost)], ok; adjust(Reason) -> - _ = [rabbit_federation_link_sup:adjust(Pid, Q, Reason) || + _ = [rabbit_federation_link_sup:adjust(Pid, rabbit_federation_queue_link, Q, Reason) || {Q, Pid, _, _} <- mirrored_supervisor:which_children(?SUPERVISOR)], ok. diff --git a/deps/rabbitmq_federation/src/rabbit_federation_app.erl b/deps/rabbitmq_queue_federation/src/rabbit_queue_federation_app.erl similarity index 77% rename from deps/rabbitmq_federation/src/rabbit_federation_app.erl rename to deps/rabbitmq_queue_federation/src/rabbit_queue_federation_app.erl index e3115066ce1b..541a59d4db0d 100644 --- a/deps/rabbitmq_federation/src/rabbit_federation_app.erl +++ b/deps/rabbitmq_queue_federation/src/rabbit_queue_federation_app.erl @@ -5,7 +5,10 @@ %% Copyright (c) 2007-2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_federation_app). +-module(rabbit_queue_federation_app). + +-include_lib("rabbitmq_federation_common/include/rabbit_federation.hrl"). +-include("rabbit_queue_federation.hrl"). -behaviour(application). -export([start/2, stop/1]). @@ -27,10 +30,14 @@ -export([init/1]). start(_Type, _StartArgs) -> + ets:insert(?FEDERATION_ETS, + {rabbitmq_queue_federation, + #{link_module => rabbit_federation_queue_link_sup_sup}}), supervisor:start_link({local, ?MODULE}, ?MODULE, []). stop(_State) -> - rabbit_federation_pg:stop_scope(), + ets:delete(?FEDERATION_ETS, rabbitmq_queue_federation), + rabbit_federation_pg:stop_scope(?FEDERATION_PG_SCOPE), ok. %%---------------------------------------------------------------------------- diff --git a/deps/rabbitmq_queue_federation/src/rabbit_queue_federation_sup.erl b/deps/rabbitmq_queue_federation/src/rabbit_queue_federation_sup.erl new file mode 100644 index 000000000000..0a37547c5bc6 --- /dev/null +++ b/deps/rabbitmq_queue_federation/src/rabbit_queue_federation_sup.erl @@ -0,0 +1,64 @@ +%% 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_queue_federation_sup). + +-behaviour(supervisor). + +%% Supervises everything. There is just one of these. + +-include_lib("rabbit_common/include/rabbit.hrl"). +-include("rabbit_queue_federation.hrl"). + +-define(SUPERVISOR, ?MODULE). + +-export([start_link/0, stop/0]). + +-export([init/1]). + +%% This supervisor needs to be part of the rabbit application since +%% a) it needs to be in place when exchange recovery takes place +%% b) it needs to go up and down with rabbit + +-rabbit_boot_step({rabbit_queue_federation_supervisor, + [{description, "federation"}, + {mfa, {rabbit_sup, start_child, [?MODULE]}}, + {requires, [kernel_ready, rabbit_federation_supervisor]}, + {cleanup, {?MODULE, stop, []}}, + {enables, rabbit_federation_queue}]}). + +%%---------------------------------------------------------------------------- + +start_link() -> + supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []). + +stop() -> + ok = supervisor:terminate_child(rabbit_sup, ?MODULE), + ok = supervisor:delete_child(rabbit_sup, ?MODULE). + +%%---------------------------------------------------------------------------- + +init([]) -> + QLinkSupSup = #{ + id => q_links, + start => {rabbit_federation_queue_link_sup_sup, start_link, []}, + restart => transient, + shutdown => ?SUPERVISOR_WAIT, + type => supervisor, + modules => [rabbit_federation_queue_link_sup_sup] + }, + %% with default reconnect-delay of 5 second, this supports up to + %% 100 links constantly failing and being restarted a minute + %% (or 200 links if reconnect-delay is 10 seconds, 600 with 30 seconds, + %% etc: N * (60/reconnect-delay) <= 1200) + Flags = #{ + strategy => one_for_one, + intensity => 1200, + period => 60 + }, + Specs = [QLinkSupSup], + {ok, {Flags, Specs}}. diff --git a/deps/rabbitmq_queue_federation/test/definition_import_SUITE.erl b/deps/rabbitmq_queue_federation/test/definition_import_SUITE.erl new file mode 100644 index 000000000000..d656d187f1e1 --- /dev/null +++ b/deps/rabbitmq_queue_federation/test/definition_import_SUITE.erl @@ -0,0 +1,104 @@ +%% 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(definition_import_SUITE). + +-include_lib("rabbitmq_ct_helpers/include/rabbit_assert.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-compile(export_all). + +all() -> + [ + {group, roundtrip} + ]. + +groups() -> + [ + {roundtrip, [], [ + export_import_round_trip + ]} + ]. + +%% ------------------------------------------------------------------- +%% Test suite setup/teardown. +%% ------------------------------------------------------------------- + +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + inets:start(), + Config. +end_per_suite(Config) -> + Config. + +init_per_group(Group, Config) -> + Config1 = rabbit_ct_helpers:set_config(Config, [ + {rmq_nodename_suffix, Group} + ]), + rabbit_ct_helpers:run_setup_steps(Config1, rabbit_ct_broker_helpers:setup_steps()). + +end_per_group(_, Config) -> + rabbit_ct_helpers:run_teardown_steps(Config, rabbit_ct_broker_helpers:teardown_steps()). + +init_per_testcase(Testcase, Config) -> + rabbit_ct_helpers:testcase_started(Config, Testcase). + +end_per_testcase(Testcase, Config) -> + rabbit_ct_helpers:testcase_finished(Config, Testcase). + +%% +%% Tests +%% + +export_import_round_trip(Config) -> + case rabbit_ct_helpers:is_mixed_versions() of + false -> + import_file_case(Config, "case1"), + Defs = export(Config), + import_raw(Config, rabbit_json:encode(Defs)); + _ -> + %% skip the test in mixed version mode + {skip, "Should not run in mixed version environments"} + end. + +%% +%% Implementation +%% + +import_file_case(Config, CaseName) -> + CasePath = filename:join([ + ?config(data_dir, Config), + CaseName ++ ".json" + ]), + rabbit_ct_broker_helpers:rpc(Config, 0, ?MODULE, run_import_case, [CasePath]), + ok. + + +import_raw(Config, Body) -> + case rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_definitions, import_raw, [Body]) of + ok -> ok; + {error, E} -> + ct:pal("Import of JSON definitions ~tp failed: ~tp~n", [Body, E]), + ct:fail({expected_failure, Body, E}) + end. + +export(Config) -> + rabbit_ct_broker_helpers:rpc(Config, 0, ?MODULE, run_export, []). + +run_export() -> + rabbit_definitions:all_definitions(). + +run_import_case(Path) -> + {ok, Body} = file:read_file(Path), + ct:pal("Successfully loaded a definition to import from ~tp~n", [Path]), + case rabbit_definitions:import_raw(Body) of + ok -> ok; + {error, E} -> + ct:pal("Import case ~tp failed: ~tp~n", [Path, E]), + ct:fail({expected_failure, Path, E}) + end. diff --git a/deps/rabbitmq_queue_federation/test/definition_import_SUITE_data/case1.json b/deps/rabbitmq_queue_federation/test/definition_import_SUITE_data/case1.json new file mode 100644 index 000000000000..e549e4fd6c1d --- /dev/null +++ b/deps/rabbitmq_queue_federation/test/definition_import_SUITE_data/case1.json @@ -0,0 +1,52 @@ +{ + "permissions": [ + { + "configure": ".*", + "read": ".*", + "user": "guest", + "vhost": "/", + "write": ".*" + } + ], + "bindings": [], + "queues": [], + "parameters": [ + { + "component": "federation-upstream-set", + "name": "location-1", + "value": [ + { + "upstream":"up-1" + }, + { + "upstream":"up-2" + } + ], + "vhost":"/"}], + "policies": [], + "rabbitmq_version": "3.13.0+376.g1bc0d89.dirty", + "users": [ + { + "hashing_algorithm": "rabbit_password_hashing_sha256", + "limits": {}, + "name": "guest", + "password_hash": "jTcCKuOmGJeeRQ/K1LG5sdZLcdnEnqv8wcrP2n68R7nMuqy2", + "tags": ["administrator"] + } + ], + "rabbit_version": "3.13.0+376.g1bc0d89.dirty", + "exchanges": [], + "topic_permissions": [], + "vhosts": [ + { + "limits": [], + "metadata": + { + "description": "Default virtual host", + "tags": [] + }, + "name":"/" + } + ], + "global_parameters": [] +} diff --git a/deps/rabbitmq_federation/test/queue_SUITE.erl b/deps/rabbitmq_queue_federation/test/queue_SUITE.erl similarity index 98% rename from deps/rabbitmq_federation/test/queue_SUITE.erl rename to deps/rabbitmq_queue_federation/test/queue_SUITE.erl index bcc7192b34ae..c8f3280ca038 100644 --- a/deps/rabbitmq_federation/test/queue_SUITE.erl +++ b/deps/rabbitmq_queue_federation/test/queue_SUITE.erl @@ -18,8 +18,7 @@ set_upstream/4, set_upstream/5, clear_upstream/3, set_upstream_set/4, clear_upstream_set/3, set_policy/5, clear_policy/3, set_policy_pattern/5, set_policy_upstream/5, q/2, with_ch/3, - maybe_declare_queue/3, delete_queue/2, - federation_links_in_vhost/3]). + maybe_declare_queue/3, delete_queue/2]). -define(INITIAL_WAIT, 6000). -define(EXPECT_FEDERATION_TIMEOUT, 30000). @@ -285,7 +284,7 @@ dynamic_plugin_stop_start(Config) -> %% Disable the plugin, the link disappears ct:pal("Stopping rabbitmq_federation"), - ok = rabbit_ct_broker_helpers:disable_plugin(Config, 0, "rabbitmq_federation"), + ok = rabbit_ct_broker_helpers:disable_plugin(Config, 0, "rabbitmq_queue_federation"), expect_no_federation(Ch, UpQ1, DownQ1), expect_no_federation(Ch, UpQ2, DownQ2), @@ -293,12 +292,11 @@ dynamic_plugin_stop_start(Config) -> maybe_declare_queue(Config, Ch, q(DownQ1, Args)), maybe_declare_queue(Config, Ch, q(DownQ2, Args)), ct:pal("Re-starting rabbitmq_federation"), - ok = rabbit_ct_broker_helpers:enable_plugin(Config, 0, "rabbitmq_federation"), + ok = rabbit_ct_broker_helpers:enable_plugin(Config, 0, "rabbitmq_queue_federation"), timer:sleep(?INITIAL_WAIT), %% Declare a queue then re-enable the plugin, the links appear - wait_for_federation( - 90, + rabbit_ct_helpers:await_condition( fun() -> Status = rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_federation_status, status, []), @@ -311,7 +309,7 @@ dynamic_plugin_stop_start(Config) -> proplists:get_value(status, Entry) =:= running ], length(L) =:= 2 - end), + end, 90000), expect_federation(Ch, UpQ1, DownQ1, 120000) end, upstream_downstream(Config) ++ [q(DownQ2, Args)]). diff --git a/deps/rabbitmq_federation/test/federation_status_command_SUITE.erl b/deps/rabbitmq_queue_federation/test/queue_federation_status_command_SUITE.erl similarity index 99% rename from deps/rabbitmq_federation/test/federation_status_command_SUITE.erl rename to deps/rabbitmq_queue_federation/test/queue_federation_status_command_SUITE.erl index 2ca0dd8c2342..84ed176d103a 100644 --- a/deps/rabbitmq_federation/test/federation_status_command_SUITE.erl +++ b/deps/rabbitmq_queue_federation/test/queue_federation_status_command_SUITE.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(federation_status_command_SUITE). +-module(queue_federation_status_command_SUITE). -include_lib("amqp_client/include/amqp_client.hrl"). diff --git a/deps/rabbitmq_federation/test/rabbit_federation_status_SUITE.erl b/deps/rabbitmq_queue_federation/test/rabbit_federation_status_SUITE.erl similarity index 89% rename from deps/rabbitmq_federation/test/rabbit_federation_status_SUITE.erl rename to deps/rabbitmq_queue_federation/test/rabbit_federation_status_SUITE.erl index 39f13f685f58..1af890cb91e0 100644 --- a/deps/rabbitmq_federation/test/rabbit_federation_status_SUITE.erl +++ b/deps/rabbitmq_queue_federation/test/rabbit_federation_status_SUITE.erl @@ -9,7 +9,7 @@ -include_lib("amqp_client/include/amqp_client.hrl"). --include("rabbit_federation.hrl"). +-include("rabbit_queue_federation.hrl"). -compile(export_all). @@ -17,8 +17,7 @@ [expect/3, expect_empty/2, set_upstream/4, clear_upstream/3, set_upstream_set/4, set_policy/5, clear_policy/3, - set_policy_upstream/5, set_policy_upstreams/4, - no_plugins/1, with_ch/3]). + with_ch/3]). all() -> [ @@ -76,7 +75,8 @@ queue_status(Config) -> fun (_Ch) -> timer:sleep(3000), [Link] = rabbit_ct_broker_helpers:rpc(Config, 0, - rabbit_federation_status, status, []), + rabbit_federation_status, status, + []), true = is_binary(proplists:get_value(id, Link)) end, queue_SUITE:upstream_downstream()). @@ -86,10 +86,12 @@ lookup_queue_status(Config) -> fun (_Ch) -> timer:sleep(3000), [Link] = rabbit_ct_broker_helpers:rpc(Config, 0, - rabbit_federation_status, status, []), + rabbit_federation_status, status, + []), Id = proplists:get_value(id, Link), Props = rabbit_ct_broker_helpers:rpc(Config, 0, - rabbit_federation_status, lookup, [Id]), + rabbit_federation_status, lookup, + [Id]), lists:all(fun(K) -> lists:keymember(K, 1, Props) end, [key, uri, status, timestamp, id, supervisor, upstream]) end, queue_SUITE:upstream_downstream()). @@ -101,5 +103,6 @@ lookup_bad_status(Config) -> timer:sleep(3000), not_found = rabbit_ct_broker_helpers:rpc( Config, 0, - rabbit_federation_status, lookup, [<<"justmadeitup">>]) + rabbit_federation_status, lookup, + [<<"justmadeitup">>]) end, queue_SUITE:upstream_downstream()). diff --git a/deps/rabbitmq_federation/test/rabbit_federation_test_util.erl b/deps/rabbitmq_queue_federation/test/rabbit_federation_test_util.erl similarity index 76% rename from deps/rabbitmq_federation/test/rabbit_federation_test_util.erl rename to deps/rabbitmq_queue_federation/test/rabbit_federation_test_util.erl index 8a49b9087645..fecfda3ed0de 100644 --- a/deps/rabbitmq_federation/test/rabbit_federation_test_util.erl +++ b/deps/rabbitmq_queue_federation/test/rabbit_federation_test_util.erl @@ -7,14 +7,12 @@ -module(rabbit_federation_test_util). --include("rabbit_federation.hrl"). +-include_lib("rabbitmq_federation_common/include/rabbit_federation.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("amqp_client/include/amqp_client.hrl"). -compile(export_all). --deprecated({wait_for_federation,2,"Use rabbit_ct_helpers:await_condition or ?awaitMatch instead"}). - -import(rabbit_misc, [pget/2]). setup_federation(Config) -> @@ -155,17 +153,6 @@ setup_down_federation(Config) -> <<"fed">>, <<"^fed1\.">>, <<"all">>, [{<<"federation-upstream-set">>, <<"upstream">>}]), Config. -wait_for_federation(Retries, Fun) -> - case Fun() of - true -> - ok; - false when Retries > 0 -> - timer:sleep(1000), - wait_for_federation(Retries - 1, Fun); - false -> - throw({timeout_while_waiting_for_federation, Fun}) - end. - expect(Ch, Q, Fun) when is_function(Fun) -> amqp_channel:subscribe(Ch, #'basic.consume'{queue = Q, no_ack = true}, self()), @@ -270,78 +257,8 @@ disambiguate(Config) -> [rabbitmq_federation, pgroup_name_cluster_id, true]), Config. -no_plugins(Cfg) -> - [{K, case K of - plugins -> none; - _ -> V - end} || {K, V} <- Cfg]. - %%---------------------------------------------------------------------------- -all_federation_links(Config, Node) -> - rabbit_ct_broker_helpers:rpc(Config, Node, rabbit_federation_status, status, []). - -federation_links_in_vhost(Config, Node, VirtualHost) -> - Links = rabbit_ct_broker_helpers:rpc(Config, Node, rabbit_federation_status, status, []), - lists:filter( - fun(Link) -> - VirtualHost =:= proplists:get_value(vhost, Link) - end, Links). - -status_fields(Prop, Statuses) -> - lists:usort( - lists:map( - fun(Link) -> proplists:get_value(Prop, Link) end, - Statuses)). - -assert_status(Config, Node, XorQs, Names) -> - rabbit_ct_broker_helpers:rpc(Config, Node, - ?MODULE, assert_status1, [XorQs, Names]). - -assert_status1(XorQs, Names) -> - [begin - ct:pal("links(XorQ) for ~tp: ~tp", [XorQ, links(XorQ)]), - ct:pal("rabbit_federation_status:status(): ~tp", [rabbit_federation_status:status()]) - end || XorQ <- XorQs], - Links = lists:append([links(XorQ) || XorQ <- XorQs]), - Remaining = lists:foldl(fun (Link, Status) -> - assert_link_status(Link, Status, Names) - end, rabbit_federation_status:status(), Links), - ?assertEqual([], Remaining), - ok. - -assert_link_status({DXorQNameBin, UpstreamName, UXorQNameBin}, Status, - {TypeName, UpstreamTypeName}) -> - {This, Rest} = lists:partition( - fun(St) -> - pget(upstream, St) =:= UpstreamName andalso - pget(TypeName, St) =:= DXorQNameBin andalso - pget(UpstreamTypeName, St) =:= UXorQNameBin - end, Status), - ?assertMatch([_], This), - Rest. - -links(#'exchange.declare'{exchange = Name}) -> - case rabbit_exchange:lookup(xr(Name)) of - {ok, X} -> - case rabbit_policy:get(<<"federation-upstream-set">>, X) of - undefined -> - case rabbit_policy:get(<<"federation-upstream-pattern">>, X) of - undefined -> []; - Regex -> - [{Name, U#upstream.name, U#upstream.exchange_name} || - U <- rabbit_federation_upstream:from_pattern(Regex, X)] - end; - Set -> - [{Name, U#upstream.name, U#upstream.exchange_name} || - U <- rabbit_federation_upstream:from_set(Set, X)] - end; - {error, not_found} -> - [] - end. - -xr(Name) -> rabbit_misc:r(<<"/">>, exchange, Name). - with_ch(Config, Fun, Methods) -> Ch = rabbit_ct_client_helpers:open_channel(Config), declare_all(Config, Ch, Methods), diff --git a/deps/rabbitmq_queue_federation/test/rabbit_queue_federation_status_SUITE.erl b/deps/rabbitmq_queue_federation/test/rabbit_queue_federation_status_SUITE.erl new file mode 100644 index 000000000000..42142dd79800 --- /dev/null +++ b/deps/rabbitmq_queue_federation/test/rabbit_queue_federation_status_SUITE.erl @@ -0,0 +1,107 @@ +%% 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_queue_federation_status_SUITE). + +-include_lib("amqp_client/include/amqp_client.hrl"). + +-include("rabbit_queue_federation.hrl"). + +-compile(export_all). + +-import(rabbit_federation_test_util, + [expect/3, expect_empty/2, + set_upstream/4, clear_upstream/3, set_upstream_set/4, + set_policy/5, clear_policy/3, + with_ch/3]). + +all() -> + [ + {group, non_parallel_tests} + ]. + +groups() -> + [ + {non_parallel_tests, [], [ + queue_status, + lookup_queue_status, + lookup_bad_status + ]} + ]. + +suite() -> + [{timetrap, {minutes, 5}}]. + +%% ------------------------------------------------------------------- +%% Testsuite setup/teardown. +%% ------------------------------------------------------------------- +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + 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() ++ + [fun rabbit_federation_test_util:setup_federation/1]). +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). + +%% ------------------------------------------------------------------- +%% Test cases +%% ------------------------------------------------------------------- + +queue_status(Config) -> + with_ch( + Config, + fun (_Ch) -> + timer:sleep(3000), + [Link] = rabbit_ct_broker_helpers:rpc( + Config, 0, rabbit_federation_status, status, + []), + true = is_binary(proplists:get_value(id, Link)) + end, queue_SUITE:upstream_downstream()). + +lookup_queue_status(Config) -> + with_ch( + Config, + fun (_Ch) -> + timer:sleep(3000), + [Link] = rabbit_ct_broker_helpers:rpc( + Config, 0, rabbit_federation_status, status, + []), + Id = proplists:get_value(id, Link), + Props = rabbit_ct_broker_helpers:rpc( + Config, 0, rabbit_federation_status, lookup, + [Id]), + lists:all(fun(K) -> lists:keymember(K, 1, Props) end, + [key, uri, status, timestamp, id, supervisor, upstream]) + end, queue_SUITE:upstream_downstream()). + +lookup_bad_status(Config) -> + with_ch( + Config, + fun (_Ch) -> + timer:sleep(3000), + not_found = rabbit_ct_broker_helpers:rpc( + Config, 0, rabbit_federation_status, lookup, + [<<"justmadeitup">>]) + end, queue_SUITE:upstream_downstream()). diff --git a/deps/rabbitmq_federation/test/restart_federation_link_command_SUITE.erl b/deps/rabbitmq_queue_federation/test/restart_federation_link_command_SUITE.erl similarity index 100% rename from deps/rabbitmq_federation/test/restart_federation_link_command_SUITE.erl rename to deps/rabbitmq_queue_federation/test/restart_federation_link_command_SUITE.erl diff --git a/selenium/test/basic-auth/enabled_plugins b/selenium/test/basic-auth/enabled_plugins index 0ec08b648cb9..c2e7ec0875a5 100644 --- a/selenium/test/basic-auth/enabled_plugins +++ b/selenium/test/basic-auth/enabled_plugins @@ -1,2 +1,2 @@ [rabbitmq_management,rabbitmq_stream,rabbitmq_stream_common,rabbitmq_stream_management, -rabbitmq_top,rabbitmq_tracing,rabbitmq_federation_management,rabbitmq_shovel_management]. +rabbitmq_top,rabbitmq_tracing,rabbitmq_queue_federation,rabbitmq_federation_management,rabbitmq_shovel_management]. diff --git a/selenium/test/multi-oauth/enabled_plugins b/selenium/test/multi-oauth/enabled_plugins index ea686b9f2b51..5fb84b6d150d 100644 --- a/selenium/test/multi-oauth/enabled_plugins +++ b/selenium/test/multi-oauth/enabled_plugins @@ -3,7 +3,7 @@ rabbitmq_auth_backend_http,rabbitmq_auth_backend_ldap, rabbitmq_auth_backend_oauth2,rabbitmq_auth_mechanism_ssl,rabbitmq_aws, rabbitmq_consistent_hash_exchange,rabbitmq_event_exchange, - rabbitmq_federation,rabbitmq_federation_management, + rabbitmq_federation_common,rabbitmq_queue_federation, rabbitmq_federation_management, rabbitmq_jms_topic_exchange,rabbitmq_management,rabbitmq_management_agent, rabbitmq_mqtt,rabbitmq_peer_discovery_aws,rabbitmq_peer_discovery_common, rabbitmq_peer_discovery_consul,rabbitmq_peer_discovery_etcd, diff --git a/selenium/test/oauth/enabled_plugins b/selenium/test/oauth/enabled_plugins index 8dbd7d6cbf63..2592cc7b89f4 100644 --- a/selenium/test/oauth/enabled_plugins +++ b/selenium/test/oauth/enabled_plugins @@ -3,7 +3,7 @@ rabbitmq_auth_backend_http,rabbitmq_auth_backend_ldap, rabbitmq_auth_backend_oauth2,rabbitmq_auth_mechanism_ssl,rabbitmq_aws, rabbitmq_consistent_hash_exchange,rabbitmq_event_exchange, - rabbitmq_federation,rabbitmq_federation_management, + rabbitmq_federation_common,rabbitmq_queue_federation,rabbitmq_federation_management, rabbitmq_federation_prometheus,rabbitmq_jms_topic_exchange, rabbitmq_management,rabbitmq_management_agent,rabbitmq_mqtt, rabbitmq_peer_discovery_aws,rabbitmq_peer_discovery_common,