Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions deps/rabbitmq_management/priv/www/js/formatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -1094,3 +1094,15 @@ function fmt_deprecation_phase(phase, deprecation_phases){
}
}
}

function fmt_resource(res) {
return `${res.kind} '${res.name}' in vhost '${res.virtual_host}'`;
}

function fmt_resource_link(res) {
if (res.kind == "queue") {
return `${res.kind} '${link_queue(res.virtual_host, res.name, {})}' in vhost '${link_vhost(res.virtual_host)}'`;
} else if (res.kind == "exchange") {
return `${res.kind} '${link_exchange(res.virtual_host, res.name, {})}' in vhost '${link_vhost(res.virtual_host)}'`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
-module('Elixir.RabbitMQ.CLI.Ctl.Commands.DeleteShovelCommand').

-include("rabbit_shovel.hrl").
-include_lib("rabbit_common/include/rabbit.hrl").

-behaviour('Elixir.RabbitMQ.CLI.CommandBehaviour').

Expand All @@ -31,7 +32,7 @@
%% Callbacks
%%----------------------------------------------------------------------------
usage() ->
<<"delete_shovel [--vhost <vhost>] <name>">>.
<<"delete_shovel [--vhost <vhost>] [--force] <name>">>.

usage_additional() ->
[
Expand All @@ -49,20 +50,24 @@ help_section() ->

validate([], _Opts) ->
{validation_failure, not_enough_args};
validate([_, _ | _], _Opts) ->
validate([_, _| _], _Opts) ->
{validation_failure, too_many_args};
validate([_], _Opts) ->
ok.

merge_defaults(A, Opts) ->
{A, maps:merge(#{vhost => <<"/">>}, Opts)}.
{A, maps:merge(#{vhost => <<"/">>,
force => false}, Opts)}.

banner([Name], #{vhost := VHost}) ->
erlang:list_to_binary(io_lib:format("Deleting shovel ~ts in vhost ~ts",
[Name, VHost])).

run([Name], #{node := Node, vhost := VHost}) ->
ActingUser = 'Elixir.RabbitMQ.CLI.Core.Helpers':cli_acting_user(),
run([Name], #{node := Node, vhost := VHost, force := Force}) ->
ActingUser = case Force of
true -> ?INTERNAL_USER;
false -> 'Elixir.RabbitMQ.CLI.Core.Helpers':cli_acting_user()
end,

case rabbit_misc:rpc_call(Node, rabbit_shovel_status, cluster_status_with_nodes, []) of
{badrpc, _} = Error ->
Expand Down Expand Up @@ -98,7 +103,7 @@ delete_shovel(ErrMsg, VHost, Name, ActingUser, Opts, Node) ->
end.

switches() ->
[].
[{force, boolean}].

aliases() ->
[].
Expand Down
25 changes: 24 additions & 1 deletion deps/rabbitmq_shovel/src/rabbit_shovel_parameters.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
src_decl_exchange/4, src_decl_queue/4, src_check_queue/4,
fields_fun/5, props_fun/9]).

-export([is_internal/1, internal_owner/1]).

-import(rabbit_misc, [pget/2, pget/3, pset/3]).

-rabbit_boot_step({?MODULE,
Expand Down Expand Up @@ -69,6 +71,17 @@ notify_clear(VHost, <<"shovel">>, Name, _Username) ->

%%----------------------------------------------------------------------------

is_internal(Def) ->
pget(<<"internal">>, Def, false).

internal_owner(Def) ->
case pget(<<"internal_owner">>, Def, undefined) of
undefined -> undefined;
Owner -> rabbit_misc:r(pget(<<"virtual_host">>, Owner),
binary_to_existing_atom(pget(<<"kind">>, Owner)),
pget(<<"name">>, Owner))
end.

validate_src(Def) ->
case protocols(Def) of
{amqp091, _} -> validate_amqp091_src(Def);
Expand Down Expand Up @@ -112,7 +125,9 @@ validate_amqp091_dest(Def) ->
end].

shovel_validation() ->
[{<<"reconnect-delay">>, fun rabbit_parameter_validation:number/2,optional},
[{<<"internal">>, fun rabbit_parameter_validation:boolean/2, optional},
{<<"internal_owner">>, fun validate_internal_owner/2, optional},
{<<"reconnect-delay">>, fun rabbit_parameter_validation:number/2,optional},
{<<"ack-mode">>, rabbit_parameter_validation:enum(
['no-ack', 'on-publish', 'on-confirm']), optional},
{<<"src-protocol">>,
Expand Down Expand Up @@ -233,6 +248,14 @@ validate_delete_after(Name, Term) ->
{error, "~ts should be a number greater than or equal to 0, \"never\" or \"queue-length\", actually was "
"~tp", [Name, Term]}.

validate_internal_owner(Name, Term0) ->
Term = rabbit_data_coercion:to_proplist(Term0),

rabbit_parameter_validation:proplist(Name, [{<<"name">>, fun rabbit_parameter_validation:binary/2},
{<<"kind">>, rabbit_parameter_validation:enum(
['exchange', 'queue'])},
{<<"virtual_host">>, fun rabbit_parameter_validation:binary/2}], Term).

validate_queue_args(Name, Term0) ->
Term = rabbit_data_coercion:to_proplist(Term0),

Expand Down
38 changes: 36 additions & 2 deletions deps/rabbitmq_shovel/src/rabbit_shovel_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
get_shovel_parameter/1]).

-include_lib("rabbit_common/include/rabbit_framing.hrl").
-include_lib("rabbit_common/include/rabbit.hrl").

-define(ROUTING_HEADER, <<"x-shovelled">>).
-define(TIMESTAMP_HEADER, <<"x-shovelled-timestamp">>).
Expand Down Expand Up @@ -45,8 +46,41 @@ delete_shovel(VHost, Name, ActingUser) ->
ok = rabbit_runtime_parameters:clear(VHost, <<"shovel">>, Name, ActingUser),
{error, not_found};
_Obj ->
rabbit_log:info("Will delete runtime parameters of shovel '~ts' in virtual host '~ts'", [Name, VHost]),
ok = rabbit_runtime_parameters:clear(VHost, <<"shovel">>, Name, ActingUser)
ShovelParameters = rabbit_runtime_parameters:value(VHost, <<"shovel">>, Name),
case needs_force_delete(ShovelParameters, ActingUser) of
false ->
rabbit_log:info("Will delete runtime parameters of shovel '~ts' in virtual host '~ts'", [Name, VHost]),
ok = rabbit_runtime_parameters:clear(VHost, <<"shovel">>, Name, ActingUser);
true ->
report_that_protected_shovel_cannot_be_deleted(Name, VHost, ShovelParameters)
end
end.

-spec report_that_protected_shovel_cannot_be_deleted(binary(), binary(), map() | [tuple()]) -> no_return().
report_that_protected_shovel_cannot_be_deleted(Name, VHost, ShovelParameters) ->
case rabbit_shovel_parameters:internal_owner(ShovelParameters) of
undefined ->
rabbit_misc:protocol_error(
resource_locked,
"Cannot delete protected shovel '~ts' in virtual host '~ts'.",
[Name, VHost]);
IOwner ->
rabbit_misc:protocol_error(
resource_locked,
"Cannot delete protected shovel '~ts' in virtual host '~ts'. It was "
"declared as protected, delete it with --force or delete its owner entity instead: ~ts",
[Name, VHost, rabbit_misc:rs(IOwner)])
end.

needs_force_delete(Parameters,ActingUser) ->
case rabbit_shovel_parameters:is_internal(Parameters) of
false ->
false;
true ->
case ActingUser of
?INTERNAL_USER -> false;
_ -> true
end
end.

restart_shovel(VHost, Name) ->
Expand Down
53 changes: 50 additions & 3 deletions deps/rabbitmq_shovel/test/delete_shovel_command_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ groups() ->
[
{non_parallel_tests, [], [
delete_not_found,
delete
delete,
delete_internal,
delete_internal_owner
]},
{cluster_size_2, [], [
clear_param_on_different_node
Expand Down Expand Up @@ -73,7 +75,7 @@ end_per_testcase(Testcase, Config) ->
%% -------------------------------------------------------------------
delete_not_found(Config) ->
[A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
Opts = #{node => A, vhost => <<"/">>},
Opts = #{node => A, vhost => <<"/">>, force => false},
{error, _} = ?CMD:run([<<"myshovel">>], Opts).

delete(Config) ->
Expand All @@ -82,10 +84,55 @@ delete(Config) ->
<<"myshovel">>, [{<<"src-queue">>, <<"src">>},
{<<"dest-queue">>, <<"dest">>}]),
[A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
Opts = #{node => A, vhost => <<"/">>},
Opts = #{node => A, vhost => <<"/">>, force => false},
ok = ?CMD:run([<<"myshovel">>], Opts),
[] = rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_shovel_status,
status, []).

delete_internal(Config) ->
shovel_test_utils:set_param(
Config,
<<"myshovel">>, [{<<"src-queue">>, <<"src">>},
{<<"internal">>, true},
{<<"dest-queue">>, <<"dest">>}]),
[A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
Opts = #{node => A, vhost => <<"/">>, force => false},
{badrpc,
{'EXIT',
{amqp_error, resource_locked,
"Cannot delete protected shovel 'myshovel' in virtual host '/'.",
none}}} = ?CMD:run([<<"myshovel">>], Opts),
[_] = rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_shovel_status,
status, []),

ForceOpts = #{node => A, vhost => <<"/">>, force => true},
ok = ?CMD:run([<<"myshovel">>], ForceOpts),
[] = rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_shovel_status,
status, []).

delete_internal_owner(Config) ->
shovel_test_utils:set_param(
Config,
<<"myshovel">>, [{<<"src-queue">>, <<"src">>},
{<<"internal">>, true},
{<<"internal_owner">>, [{<<"name">>, <<"src">>},
{<<"kind">>, <<"queue">>},
{<<"virtual_host">>, <<"/">>}]},
{<<"dest-queue">>, <<"dest">>}]),
[A] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
Opts = #{node => A, vhost => <<"/">>, force => false},
?assertMatch(
{badrpc, {'EXIT', {amqp_error, resource_locked, _, none}}},
?CMD:run([<<"myshovel">>], Opts)
),
[_] = rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_shovel_status,
status, []),

ForceOpts = #{node => A, vhost => <<"/">>, force => true},
ok = ?CMD:run([<<"myshovel">>], ForceOpts),
[] = rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_shovel_status,
status, []).

clear_param_on_different_node(Config) ->
shovel_test_utils:set_param(
Config,
Expand Down
21 changes: 21 additions & 0 deletions deps/rabbitmq_shovel_management/priv/www/js/shovel.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,27 @@ function fmt_shovel_endpoint(prefix, shovel) {
return txt;
}

function is_internal_shovel(shovel) {
if (!shovel.hasOwnProperty('internal')) {
return false;
} else {
return shovel['internal'];
}
}

function shovel_has_internal_owner(shovel) {
if (!shovel.hasOwnProperty('internal_owner')) {
return false;
} else {
return true;
}
}

function shovel_internal_owner(shovel) {
return shovel.internal_owner;
}


function fallback_value(shovel, key1, key2) {
var v = shovel.value[key1];
return (v !== undefined ? v : shovel.value[key2]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,23 @@
</div>
</div>

<div class="section-hidden">

<div class="section-hidden">
<h2>Delete this shovel</h2>
<div class="hider">
<form action="#/shovel-parameters" method="delete" class="confirm">
<input type="hidden" name="component" value="shovel"/>
<input type="hidden" name="vhost" value="<%= fmt_string(shovel.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_string(shovel.name) %>"/>
<input type="submit" value="Delete this shovel"/>
</form>
<% if (!is_internal_shovel(shovel.value)) { %>
<form action="#/shovel-parameters" method="delete" class="confirm">
<input type="hidden" name="component" value="shovel"/>
<input type="hidden" name="vhost" value="<%= fmt_string(shovel.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_string(shovel.name) %>"/>
<input type="submit" value="Delete this shovel"/>
</form>
<% } else { %>
<% if (shovel_has_internal_owner(shovel.value)) { %>
<span>This shovel is internal and owned by <%= fmt_resource_link(shovel_internal_owner(shovel.value)) %>. Could be deleted only via CLI command with --force.</span>
<% } else { %>
<span>This shovel is internal. Could be deleted only via CLI command with '--force'.</span>
<% } %>
<% } %>
</div>
</div>
</div>
Loading
Loading