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; 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) { function fallback_value(shovel, key1, key2) {
var v = shovel.value[key1]; var v = shovel.value[key1];
return (v !== undefined ? v : shovel.value[key2]); return (v !== undefined ? v : shovel.value[key2]);

View File

@ -44,14 +44,23 @@
</div> </div>
</div> </div>
<div class="section-hidden">
<div class="section-hidden">
<h2>Delete this shovel</h2> <h2>Delete this shovel</h2>
<div class="hider"> <div class="hider">
<form action="#/shovel-parameters" method="delete" class="confirm"> <% if (!is_internal_shovel(shovel.value)) { %>
<input type="hidden" name="component" value="shovel"/> <form action="#/shovel-parameters" method="delete" class="confirm">
<input type="hidden" name="vhost" value="<%= fmt_string(shovel.vhost) %>"/> <input type="hidden" name="component" value="shovel"/>
<input type="hidden" name="name" value="<%= fmt_string(shovel.name) %>"/> <input type="hidden" name="vhost" value="<%= fmt_string(shovel.vhost) %>"/>
<input type="submit" value="Delete this shovel"/> <input type="hidden" name="name" value="<%= fmt_string(shovel.name) %>"/>
</form> <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>
</div>

View File

@ -79,45 +79,60 @@ is_authorized(ReqData, Context) ->
delete_resource(ReqData, #context{user = #user{username = Username}}=Context) -> delete_resource(ReqData, #context{user = #user{username = Username}}=Context) ->
VHost = rabbit_mgmt_util:id(vhost, ReqData), 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 -> none ->
false; {false, ReqData, Context};
Name -> Name ->
case get_shovel_node(VHost, Name, ReqData, Context) of 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]), undefined -> rabbit_log:error("Could not find shovel data for shovel '~ts' in vhost: '~ts'", [Name, VHost]),
case is_restart(ReqData) of case is_restart(ReqData) of
true -> true ->
false; {false, ReqData, Context};
%% this is a deletion attempt %% this is a deletion attempt
false -> false ->
%% if we do not know the node, use the local one %% if we do not know the node, use the local one
try_delete(node(), VHost, Name, Username), case try_delete(node(), VHost, Name, Username) of
true 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
case is_restart(ReqData) of
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, ReqData, Context};
{error, not_found} ->
rabbit_log:error("Could not find shovel data for shovel '~s' in vhost: '~s'", [Name, VHost]),
{false, ReqData, Context}
catch _:Reason ->
rabbit_log:error("Failed to restart shovel '~s' on vhost '~s', reason: ~p",
[Name, VHost, Reason]),
{false, ReqData, Context}
end; end;
Node ->
%% We must distinguish between a delete and a restart
case is_restart(ReqData) of
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;
{error, not_found} ->
rabbit_log:error("Could not find shovel data for shovel '~s' in vhost: '~s'", [Name, VHost]),
false
catch _:Reason ->
rabbit_log:error("Failed to restart shovel '~s' on vhost '~s', reason: ~p",
[Name, VHost, Reason]),
false
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
end, end
{Reply, ReqData, Context}. end.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -168,7 +183,7 @@ find_matching_shovel(VHost, Name, Shovels) ->
undefined undefined
end. 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) -> try_delete(Node, VHost, Name, Username) ->
rabbit_log:info("Asked to delete shovel '~ts' in vhost '~ts' on node '~s'", [Name, VHost, Node]), 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. %% 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} -> {error, not_found} ->
rabbit_log:error("Could not find shovel data for shovel '~s' in vhost: '~s'", [Name, VHost]), rabbit_log:error("Could not find shovel data for shovel '~s' in vhost: '~s'", [Name, VHost]),
false false
catch _:Reason -> catch
_:{exception, {amqp_error, resource_locked, Reason, _}} ->
rabbit_log:error("Failed to delete shovel '~s' on vhost '~s', reason: ~p", rabbit_log:error("Failed to delete shovel '~s' on vhost '~s', reason: ~p",
[Name, VHost, Reason]), [Name, VHost, Reason]),
false locked;
_:Reason ->
rabbit_log:error("Failed to delete shovel '~s' on vhost '~s', reason: ~p",
[Name, VHost, Reason]),
error
end. end.