Management part for internal shovels. Experimenting with owner linking

This commit is contained in:
Iliia Khaprov 2025-06-15 15:03:01 +02:00
parent 7a34bf8053
commit f13f99303a
No known key found for this signature in database
GPG Key ID: 4DCFF8F358E49AED
4 changed files with 108 additions and 46 deletions

View File

@ -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)}'`;
}
}

View File

@ -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]);

View File

@ -44,14 +44,23 @@
</div>
</div>
<div class="section-hidden">
<h2>Delete this shovel</h2>
<div class="hider">
<% 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>

View File

@ -79,20 +79,28 @@ is_authorized(ReqData, Context) ->
delete_resource(ReqData, #context{user = #user{username = Username}}=Context) ->
VHost = rabbit_mgmt_util:id(vhost, ReqData),
Reply = case rabbit_mgmt_util:id(name, ReqData) of
case rabbit_mgmt_util:id(name, ReqData) of
none ->
false;
{false, ReqData, Context};
Name ->
case get_shovel_node(VHost, Name, ReqData, Context) of
undefined -> rabbit_log:error("Could not find shovel data for shovel '~ts' in vhost: '~ts'", [Name, VHost]),
case is_restart(ReqData) of
true ->
false;
{false, ReqData, Context};
%% this is a deletion attempt
false ->
%% if we do not know the node, use the local one
try_delete(node(), VHost, Name, Username),
true
case try_delete(node(), VHost, Name, Username) of
true -> {true, ReqData, Context};
%% NOTE: that how it was before, try_delete return was ignored and true returned ¯\_()_/¯
false -> {true, ReqData, Context};
locked -> Reply = cowboy_req:reply(405, #{<<"content-type">> => <<"text/plain">>},
"Protected", ReqData),
{halt, Reply, Context};
%% NOTE: that how it was before, try_delete return was ignored and true returned ¯\_()_/¯
error -> {true, ReqData, Context}
end
end;
Node ->
%% We must distinguish between a delete and a restart
@ -100,24 +108,31 @@ delete_resource(ReqData, #context{user = #user{username = Username}}=Context) ->
true ->
rabbit_log:info("Asked to restart shovel '~ts' in vhost '~ts' on node '~s'", [Name, VHost, Node]),
try erpc:call(Node, rabbit_shovel_util, restart_shovel, [VHost, Name], ?SHOVEL_CALLS_TIMEOUT_MS) of
ok -> true;
ok -> {true, ReqData, Context};
{error, not_found} ->
rabbit_log:error("Could not find shovel data for shovel '~s' in vhost: '~s'", [Name, VHost]),
false
{false, ReqData, Context}
catch _:Reason ->
rabbit_log:error("Failed to restart shovel '~s' on vhost '~s', reason: ~p",
[Name, VHost, Reason]),
false
{false, ReqData, Context}
end;
_ ->
try_delete(Node, VHost, Name, Username),
true
case try_delete(Node, VHost, Name, Username) of
true -> {true, ReqData, Context};
%% NOTE: that how it was before, try_delete return was ignored and true returned ¯\_()_/¯
false -> {true, ReqData, Context};
locked -> Reply = cowboy_req:reply(405, #{<<"content-type">> => <<"text/plain">>},
"Protected", ReqData),
{halt, Reply, Context};
%% NOTE: that how it was before, try_delete return was ignored and true returned ¯\_()_/¯
error -> {true, ReqData, Context}
end
end
end
end,
{Reply, ReqData, Context}.
end.
%%--------------------------------------------------------------------
@ -168,7 +183,7 @@ find_matching_shovel(VHost, Name, Shovels) ->
undefined
end.
-spec try_delete(node(), vhost:name(), any(), rabbit_types:username()) -> boolean().
-spec try_delete(node(), vhost:name(), any(), rabbit_types:username()) -> true | false | locked | error.
try_delete(Node, VHost, Name, Username) ->
rabbit_log:info("Asked to delete shovel '~ts' in vhost '~ts' on node '~s'", [Name, VHost, Node]),
%% this will clear the runtime parameter, the ultimate way of deleting a dynamic Shovel eventually. MK.
@ -177,8 +192,13 @@ try_delete(Node, VHost, Name, Username) ->
{error, not_found} ->
rabbit_log:error("Could not find shovel data for shovel '~s' in vhost: '~s'", [Name, VHost]),
false
catch _:Reason ->
catch
_:{exception, {amqp_error, resource_locked, Reason, _}} ->
rabbit_log:error("Failed to delete shovel '~s' on vhost '~s', reason: ~p",
[Name, VHost, Reason]),
false
locked;
_:Reason ->
rabbit_log:error("Failed to delete shovel '~s' on vhost '~s', reason: ~p",
[Name, VHost, Reason]),
error
end.