2020-07-12 02:23:07 +08:00
## 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/.
2016-02-03 23:01:32 +08:00
##
2023-01-02 12:17:36 +08:00
## Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
2016-02-03 23:01:32 +08:00
2016-02-03 22:42:45 +08:00
defmodule RabbitMQCtl do
2019-01-31 03:20:29 +08:00
alias RabbitMQ.CLI.Core . {
CommandModules ,
Config ,
Distribution ,
ExitCodes ,
Helpers ,
Output ,
Parser
}
2022-10-03 01:54:11 +08:00
2019-03-18 12:23:26 +08:00
alias RabbitMQ.CLI . { CommandBehaviour , FormatterBehaviour }
2019-01-31 03:20:29 +08:00
alias RabbitMQ.CLI.Ctl.Commands.HelpCommand
2017-05-24 19:08:03 +08:00
# Enable unit tests for private functions
2019-01-31 03:20:29 +08:00
@compile if Mix . env ( ) == :test , do : :export_all
2017-05-24 19:08:03 +08:00
2017-08-14 21:36:38 +08:00
@type options ( ) :: map ( )
2019-01-31 03:20:29 +08:00
@type command_result ( ) :: { :error , ExitCodes . exit_code ( ) , term ( ) } | term ( )
2016-12-05 22:49:56 +08:00
2023-02-02 05:42:43 +08:00
@spec main ( list ( ) ) :: no_return ( )
2017-01-24 22:40:31 +08:00
def main ( [ " --auto-complete " | [ ] ] ) do
handle_shutdown ( :ok )
end
2019-01-31 03:20:29 +08:00
2016-05-09 18:07:46 +08:00
def main ( unparsed_command ) do
2017-06-06 19:07:34 +08:00
exec_command ( unparsed_command , & process_output / 3 )
2016-12-23 20:12:33 +08:00
|> handle_shutdown
end
2019-02-02 00:47:09 +08:00
def exec_command ( [ ] = unparsed_command , _ ) do
{ _args , parsed_options , _ } = Parser . parse_global ( unparsed_command )
# this invocation is considered to be invalid. curl and grep do the
# same thing.
2022-10-03 01:54:11 +08:00
{ :error , ExitCodes . exit_usage ( ) , Enum . join ( HelpCommand . all_usage ( parsed_options ) , " " ) }
2019-02-02 00:47:09 +08:00
end
2022-10-03 01:54:11 +08:00
2019-02-02 00:47:09 +08:00
def exec_command ( [ " --help " ] = unparsed_command , _ ) do
{ _args , parsed_options , _ } = Parser . parse_global ( unparsed_command )
# the user asked for --help and we are displaying it to her,
# reporting a success
2022-10-03 01:54:11 +08:00
{ :ok , ExitCodes . exit_ok ( ) , Enum . join ( HelpCommand . all_usage ( parsed_options ) , " " ) }
2019-02-02 00:47:09 +08:00
end
2022-10-03 01:54:11 +08:00
2023-02-02 05:42:43 +08:00
def exec_command ( [ " --version " ] = _unparsed_command , output_fun ) do
2019-02-14 20:10:22 +08:00
# rewrite `--version` as `version`
2023-02-02 05:42:43 +08:00
exec_command ( [ " version " ] , output_fun )
2019-02-14 20:10:22 +08:00
end
2022-10-03 01:54:11 +08:00
2023-02-02 05:42:43 +08:00
def exec_command ( [ " --auto-complete " | args ] , output_fun ) do
2020-07-02 12:29:40 +08:00
# rewrite `--auto-complete` as `autocomplete`
2023-02-02 05:42:43 +08:00
exec_command ( [ " autocomplete " | args ] , output_fun )
2020-07-02 12:29:40 +08:00
end
2022-10-03 01:54:11 +08:00
2017-06-06 19:07:34 +08:00
def exec_command ( unparsed_command , output_fun ) do
2019-01-31 03:20:29 +08:00
{ command , command_name , arguments , parsed_options , invalid } = Parser . parse ( unparsed_command )
2016-11-23 01:40:45 +08:00
case { command , invalid } do
{ :no_command , _ } ->
2019-01-31 03:20:29 +08:00
command_not_found_string =
case command_name do
" " -> " "
_ -> " \n Command ' #{ command_name } ' not found. \n "
end
usage_string =
command_not_found_string <>
2020-06-29 08:57:52 +08:00
Enum . join ( HelpCommand . all_usage ( parsed_options ) , " " )
2019-01-31 03:20:29 +08:00
{ :error , ExitCodes . exit_usage ( ) , usage_string }
2016-12-06 02:59:58 +08:00
{ { :suggest , suggested } , _ } ->
2019-01-31 03:20:29 +08:00
suggest_message =
" \n Command ' #{ command_name } ' not found. \n " <>
" Did you mean ' #{ suggested } '? \n "
{ :error , ExitCodes . exit_usage ( ) , suggest_message }
{ _ , [ _ | _ ] } ->
argument_validation_error_output (
{ :bad_option , invalid } ,
command ,
unparsed_command ,
parsed_options
)
2016-12-23 20:12:33 +08:00
2016-11-23 01:40:45 +08:00
_ ->
2018-11-30 23:00:18 +08:00
options = parsed_options |> merge_all_defaults |> normalise_options
2019-01-31 03:20:29 +08:00
2019-04-25 22:15:25 +08:00
try do
do_exec_parsed_command ( unparsed_command , output_fun , arguments , command , options )
2022-10-03 01:54:11 +08:00
catch
error_type , error ->
maybe_print_stacktrace ( error_type , error , __STACKTRACE__ , options )
format_error ( error , options , command )
2019-04-25 22:15:25 +08:00
end
end
end
2019-01-31 05:45:52 +08:00
2019-04-25 22:15:25 +08:00
def do_exec_parsed_command ( unparsed_command , output_fun , arguments , command , options ) do
case options [ :help ] do
true ->
2022-10-03 01:54:11 +08:00
{ :ok , ExitCodes . exit_ok ( ) , HelpCommand . command_usage ( command , options ) }
2019-04-25 22:15:25 +08:00
_ ->
{ arguments , options } = command . merge_defaults ( arguments , options )
maybe_with_distribution ( command , options , fn ->
# rabbitmq/rabbitmq-cli#278
case Helpers . normalise_node_option ( options ) do
{ :error , _ } = err ->
format_error ( err , options , command )
2022-10-03 01:54:11 +08:00
2019-04-25 22:15:25 +08:00
{ :ok , options } ->
# The code below implements a tiny decision tree that has
# to do with CLI argument and environment state validation.
case command . validate ( arguments , options ) do
:ok ->
# then optionally validate execution environment
2022-10-03 01:54:11 +08:00
case CommandBehaviour . validate_execution_environment (
command ,
arguments ,
options
) do
2019-04-25 22:15:25 +08:00
:ok ->
result = proceed_to_execution ( command , arguments , options )
handle_command_output ( result , command , options , output_fun )
2019-01-31 05:45:52 +08:00
{ :validation_failure , err } ->
2019-04-25 22:15:25 +08:00
environment_validation_error_output ( err , command , unparsed_command , options )
2019-01-31 05:45:52 +08:00
{ :error , _ } = err ->
format_error ( err , options , command )
end
2019-04-25 22:15:25 +08:00
{ :validation_failure , err } ->
argument_validation_error_output ( err , command , unparsed_command , options )
{ :error , _ } = err ->
format_error ( err , options , command )
2017-08-04 20:08:37 +08:00
end
2019-04-25 22:15:25 +08:00
end
end )
2016-11-04 02:10:18 +08:00
end
end
2016-06-08 22:19:37 +08:00
2017-08-04 20:08:37 +08:00
defp proceed_to_execution ( command , arguments , options ) do
2023-01-31 22:05:52 +08:00
_ = maybe_print_banner ( command , arguments , options )
2017-08-04 20:08:37 +08:00
maybe_run_command ( command , arguments , options )
end
2017-08-04 19:09:37 +08:00
defp maybe_run_command ( _ , _ , %{ dry_run : true } ) do
:ok
end
2019-01-31 03:20:29 +08:00
2017-08-04 19:09:37 +08:00
defp maybe_run_command ( command , arguments , options ) do
try do
command . run ( arguments , options ) |> command . output ( options )
2022-10-03 01:54:11 +08:00
catch
error_type , error ->
2019-11-19 06:39:50 +08:00
maybe_print_stacktrace ( error_type , error , __STACKTRACE__ , options )
2019-01-31 03:20:29 +08:00
format_error ( error , options , command )
2017-08-04 19:09:37 +08:00
end
end
2019-11-19 09:39:29 +08:00
def maybe_print_stacktrace ( error_type , :undef = error , stacktrace , _opts ) do
do_print_stacktrace ( error_type , error , stacktrace )
end
2022-10-03 01:54:11 +08:00
2019-11-19 09:39:29 +08:00
def maybe_print_stacktrace ( error_type , :function_clause = error , stacktrace , _opts ) do
do_print_stacktrace ( error_type , error , stacktrace )
end
2022-10-03 01:54:11 +08:00
2019-11-19 09:39:29 +08:00
def maybe_print_stacktrace ( error_type , :badarg = error , stacktrace , _opts ) do
do_print_stacktrace ( error_type , error , stacktrace )
end
2022-10-03 01:54:11 +08:00
2019-11-19 09:39:29 +08:00
def maybe_print_stacktrace ( error_type , { :case_clause , _val } = error , stacktrace , _opts ) do
do_print_stacktrace ( error_type , error , stacktrace )
end
2022-10-03 01:54:11 +08:00
2019-11-19 06:39:50 +08:00
def maybe_print_stacktrace ( error_type , error , stacktrace , %{ print_stacktrace : true } ) do
2019-11-19 09:39:29 +08:00
do_print_stacktrace ( error_type , error , stacktrace )
2019-04-18 23:14:02 +08:00
end
2022-10-03 01:54:11 +08:00
2019-11-19 06:39:50 +08:00
def maybe_print_stacktrace ( _error_type , _error , _stacktrace , %{ print_stacktrace : false } ) do
2019-04-18 23:14:02 +08:00
nil
end
2022-10-03 01:54:11 +08:00
2019-11-19 06:39:50 +08:00
def maybe_print_stacktrace ( _error_type , _error , _stacktrace , _opts ) do
2019-04-19 09:10:42 +08:00
nil
end
2019-04-18 23:14:02 +08:00
2019-11-19 09:39:29 +08:00
defp do_print_stacktrace ( error_type , error , stacktrace ) do
IO . puts ( " Stack trace: \n " )
IO . puts ( Exception . format ( error_type , error , stacktrace ) )
end
2017-08-09 06:11:14 +08:00
def handle_command_output ( output , command , options , output_fun ) do
2017-05-24 19:08:03 +08:00
case output do
{ :error , _ , _ } = err ->
2019-05-29 20:59:59 +08:00
format_error ( err , options , command )
2019-01-31 03:20:29 +08:00
2017-05-24 19:08:03 +08:00
{ :error , _ } = err ->
2019-01-31 03:20:29 +08:00
format_error ( err , options , command )
_ ->
2017-06-06 19:07:34 +08:00
output_fun . ( output , command , options )
2017-05-24 19:08:03 +08:00
end
end
defp process_output ( output , command , options ) do
2019-01-24 16:11:04 +08:00
formatter = Config . get_formatter ( command , options )
2019-06-27 05:32:41 +08:00
printer = Config . get_printer ( command , options )
2017-05-24 19:08:03 +08:00
output
|> Output . format_output ( formatter , options )
|> Output . print_output ( printer , options )
2017-06-06 19:07:34 +08:00
|> case do
2019-01-31 03:20:29 +08:00
{ :error , _ } = err -> format_error ( err , options , command )
other -> other
end
2017-05-24 19:08:03 +08:00
end
2019-01-22 12:43:25 +08:00
defp output_device ( exit_code ) do
2019-01-31 03:20:29 +08:00
case exit_code == ExitCodes . exit_ok ( ) do
true -> :stdio
2016-11-04 19:11:03 +08:00
false -> :stderr
end
2019-01-22 12:43:25 +08:00
end
2023-02-02 05:42:43 +08:00
@spec handle_shutdown ( { :error , integer ( ) , nil } | atom ( ) ) :: no_return ( )
2019-01-22 12:43:25 +08:00
defp handle_shutdown ( { :error , exit_code , nil } ) do
exit_program ( exit_code )
end
2019-01-31 03:20:29 +08:00
2019-02-02 00:47:09 +08:00
defp handle_shutdown ( { _ , exit_code , output } ) do
2019-01-22 12:43:25 +08:00
device = output_device ( exit_code )
2017-04-05 02:23:50 +08:00
2023-01-31 22:05:52 +08:00
Enum . each ( List . flatten ( [ output ] ) , fn line ->
2019-01-22 12:43:25 +08:00
IO . puts ( device , Helpers . string_or_inspect ( line ) )
2023-01-31 22:05:52 +08:00
end )
2019-01-31 03:20:29 +08:00
2016-11-04 02:10:18 +08:00
exit_program ( exit_code )
end
2019-01-31 03:20:29 +08:00
2017-05-24 19:08:03 +08:00
defp handle_shutdown ( _ ) do
2019-01-31 03:20:29 +08:00
exit_program ( ExitCodes . exit_ok ( ) )
2016-02-03 05:32:35 +08:00
end
2016-08-09 02:43:09 +08:00
def merge_all_defaults ( %{ } = options ) do
2016-03-08 03:08:38 +08:00
options
2018-12-01 06:48:47 +08:00
|> merge_defaults_node
2016-05-24 19:33:11 +08:00
|> merge_defaults_timeout
2016-06-08 22:19:37 +08:00
|> merge_defaults_longnames
2016-03-08 03:08:38 +08:00
end
2018-12-04 02:04:10 +08:00
defp merge_defaults_node ( %{ } = opts ) do
longnames_opt = Config . get_option ( :longnames , opts )
2022-10-03 01:54:11 +08:00
2019-04-25 22:15:25 +08:00
try do
default_rabbit_nodename = Helpers . get_rabbit_hostname ( longnames_opt )
Map . merge ( %{ node : default_rabbit_nodename } , opts )
2022-10-03 01:54:11 +08:00
catch
_error_type , _err ->
opts
2019-04-25 22:15:25 +08:00
end
2018-12-04 02:04:10 +08:00
end
2018-12-01 06:48:47 +08:00
2016-05-24 19:33:11 +08:00
defp merge_defaults_timeout ( %{ } = opts ) , do : Map . merge ( %{ timeout : :infinity } , opts )
2016-03-08 06:45:50 +08:00
2016-06-08 22:19:37 +08:00
defp merge_defaults_longnames ( %{ } = opts ) , do : Map . merge ( %{ longnames : false } , opts )
2018-11-30 23:00:18 +08:00
defp normalise_options ( opts ) do
opts |> normalise_timeout
2016-06-10 05:55:59 +08:00
end
2018-11-30 23:00:18 +08:00
defp normalise_timeout ( %{ timeout : timeout } = opts )
2019-01-31 03:20:29 +08:00
when is_integer ( timeout ) do
2016-11-11 01:40:43 +08:00
Map . put ( opts , :timeout , timeout * 1000 )
end
2019-01-31 03:20:29 +08:00
2018-11-30 23:00:18 +08:00
defp normalise_timeout ( opts ) do
2016-11-11 01:40:43 +08:00
opts
end
2017-07-14 23:08:17 +08:00
# Suppress banner if --quiet option is provided
2016-10-26 23:25:39 +08:00
defp maybe_print_banner ( _ , _ , %{ quiet : true } ) do
nil
end
2019-01-31 03:20:29 +08:00
2018-11-16 19:51:48 +08:00
# Suppress banner if --silent option is provided
defp maybe_print_banner ( _ , _ , %{ silent : true } ) do
nil
end
2019-01-31 03:20:29 +08:00
2016-10-26 23:25:39 +08:00
defp maybe_print_banner ( command , args , opts ) do
2019-03-18 12:23:26 +08:00
# Suppress banner if a machine-readable formatter is used
formatter = Map . get ( opts , :formatter )
2022-10-03 01:54:11 +08:00
2019-03-18 12:23:26 +08:00
case FormatterBehaviour . machine_readable? ( formatter ) do
2022-10-03 01:54:11 +08:00
true ->
nil
2019-03-18 12:23:26 +08:00
false ->
case command . banner ( args , opts ) do
nil ->
nil
banner ->
case banner do
list when is_list ( list ) ->
for line <- list , do : IO . puts ( line )
binary when is_binary ( binary ) ->
IO . puts ( binary )
end
2016-10-26 23:25:39 +08:00
end
2016-05-31 16:40:48 +08:00
end
2016-05-24 19:33:11 +08:00
end
2017-08-09 06:11:14 +08:00
def argument_validation_error_output ( err_detail , command , unparsed_command , options ) do
2017-08-08 23:29:04 +08:00
err = format_validation_error ( err_detail )
2019-01-31 03:20:29 +08:00
base_error =
2022-10-03 01:54:11 +08:00
" Error (argument validation): #{ err } \n Arguments given: \n \t #{ unparsed_command |> Enum . join ( " " ) } "
2019-01-31 03:20:29 +08:00
2017-08-08 23:29:04 +08:00
validation_error_output ( err_detail , base_error , command , options )
end
2017-08-09 06:11:14 +08:00
def environment_validation_error_output ( err_detail , command , unparsed_command , options ) do
2017-08-08 23:29:04 +08:00
err = format_validation_error ( err_detail )
base_error = " Error: #{ err } \n Arguments given: \n \t #{ unparsed_command |> Enum . join ( " " ) } "
validation_error_output ( err_detail , base_error , command , options )
end
defp validation_error_output ( err_detail , base_error , command , options ) do
2016-12-23 20:12:33 +08:00
usage = HelpCommand . base_usage ( command , options )
2016-11-04 19:11:03 +08:00
message = base_error <> " \n " <> usage
{ :error , ExitCodes . exit_code_for ( { :validation_failure , err_detail } ) , message }
2016-04-02 01:58:47 +08:00
end
2016-12-23 20:12:33 +08:00
defp format_validation_error ( :not_enough_args ) , do : " not enough arguments. "
2019-02-22 00:11:30 +08:00
defp format_validation_error ( { :not_enough_args , detail } ) , do : " not enough arguments: #{ detail } "
2016-12-23 20:12:33 +08:00
defp format_validation_error ( :too_many_args ) , do : " too many arguments. "
2019-02-22 00:11:30 +08:00
defp format_validation_error ( { :too_many_args , detail } ) , do : " too many arguments: #{ detail } "
2016-12-23 20:12:33 +08:00
defp format_validation_error ( :bad_argument ) , do : " Bad argument. "
2019-02-22 00:11:30 +08:00
defp format_validation_error ( { :bad_argument , detail } ) , do : " Bad argument: #{ detail } "
2019-01-31 03:20:29 +08:00
2019-02-07 09:24:13 +08:00
defp format_validation_error ( { :unsupported_target , details } ) do
details
end
2016-12-23 20:12:33 +08:00
defp format_validation_error ( { :bad_option , opts } ) do
header = " Invalid options for this command: "
2019-01-31 03:20:29 +08:00
Enum . join (
[
header
| for { key , val } <- opts do
" #{ key } : #{ val } "
end
] ,
" \n "
)
2016-08-18 21:56:21 +08:00
end
2019-01-31 03:20:29 +08:00
defp format_validation_error ( { :bad_info_key , keys } ) ,
do : " Info key(s) #{ Enum . join ( keys , " , " ) } are not supported "
defp format_validation_error ( :rabbit_app_is_stopped ) ,
do :
" this command requires the 'rabbit' app to be running on the target node. Start it with 'rabbitmqctl start_app'. "
defp format_validation_error ( :rabbit_app_is_running ) ,
do :
" this command requires the 'rabbit' app to be stopped on the target node. Stop it with 'rabbitmqctl stop_app'. "
defp format_validation_error ( :node_running ) ,
do : " this command requires the target node to be stopped. "
defp format_validation_error ( :node_not_running ) ,
do : " this command requires the target node to be running. "
2020-01-18 07:06:04 +08:00
defp format_validation_error ( :unsupported_formatter ) ,
do : " the requested formatter is not supported by this command "
2023-05-02 14:55:31 +08:00
defp format_validation_error ( { :not_a_cluster_member , potential_member } ) ,
do : " node #{ potential_member } is not a member of the cluster "
2019-01-31 03:20:29 +08:00
defp format_validation_error ( err ) , do : inspect ( err )
2016-03-23 00:21:12 +08:00
2023-01-31 22:05:52 +08:00
@spec exit_program ( integer ( ) ) :: no_return ( )
2016-03-23 00:21:12 +08:00
defp exit_program ( code ) do
2023-01-31 22:05:52 +08:00
_ = :net_kernel . stop ( )
2016-04-02 00:13:01 +08:00
exit ( { :shutdown , code } )
2016-03-23 00:21:12 +08:00
end
2016-12-05 22:49:56 +08:00
2019-04-25 22:15:25 +08:00
defp format_error ( { :error , { :node_name , :hostname_not_allowed } } , _ , _ ) do
{ :error , ExitCodes . exit_dataerr ( ) ,
2022-10-03 01:54:11 +08:00
" Unsupported node name: hostname is invalid (possibly contains unsupported characters). \n If using FQDN node names, use the -l / --longnames argument. " }
2019-04-25 22:15:25 +08:00
end
2022-10-03 01:54:11 +08:00
2019-04-26 01:13:39 +08:00
defp format_error ( { :error , { :node_name , :invalid_node_name_head } } , _ , _ ) do
2019-04-25 22:15:25 +08:00
{ :error , ExitCodes . exit_dataerr ( ) ,
2022-10-03 01:54:11 +08:00
" Unsupported node name: node name head (the part before the @) is invalid. Only alphanumerics, _ and - characters are allowed. \n If using FQDN node names, use the -l / --longnames argument " }
2019-04-25 22:15:25 +08:00
end
2022-10-03 01:54:11 +08:00
2019-02-06 00:56:04 +08:00
defp format_error ( { :error , { :node_name , err_reason } = result } , opts , module ) do
op = CommandModules . module_to_command ( module )
2019-04-25 22:15:25 +08:00
node = opts [ :node ] || " (failed to parse or compute default value) "
2022-10-03 01:54:11 +08:00
2019-02-06 00:56:04 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
2022-10-03 01:54:11 +08:00
" Error: operation #{ op } failed due to invalid node name (node: #{ node } , reason: #{ err_reason } ). \n If using FQDN node names, use the -l / --longnames argument " }
2019-02-06 00:56:04 +08:00
end
2017-07-05 03:34:39 +08:00
defp format_error ( { :error , { :badrpc_multi , :nodedown , [ node | _ ] } = result } , opts , _ ) do
2017-06-16 19:41:11 +08:00
diagnostics = get_node_diagnostics ( node )
2019-01-31 03:20:29 +08:00
2017-06-16 19:41:11 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
2017-07-05 03:34:39 +08:00
badrpc_error_message_header ( node , opts ) <> diagnostics }
2017-06-16 19:41:11 +08:00
end
2019-01-31 03:20:29 +08:00
2017-06-16 22:55:31 +08:00
defp format_error ( { :error , { :badrpc_multi , :timeout , [ node | _ ] } = result } , opts , module ) do
2017-06-16 21:51:47 +08:00
op = CommandModules . module_to_command ( module )
2019-01-31 03:20:29 +08:00
2017-06-16 19:41:11 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
2017-07-05 03:34:39 +08:00
" Error: operation #{ op } on node #{ node } timed out. Timeout value used: #{ opts [ :timeout ] } " }
2017-06-16 19:41:11 +08:00
end
2019-01-31 03:20:29 +08:00
2017-05-24 19:08:03 +08:00
defp format_error ( { :error , { :badrpc , :nodedown } = result } , opts , _ ) do
diagnostics = get_node_diagnostics ( opts [ :node ] )
2019-01-31 03:20:29 +08:00
2017-05-24 19:08:03 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
2017-07-05 03:34:39 +08:00
badrpc_error_message_header ( opts [ :node ] , opts ) <> diagnostics }
2017-05-24 19:08:03 +08:00
end
2019-01-31 03:20:29 +08:00
2017-05-24 19:08:03 +08:00
defp format_error ( { :error , { :badrpc , :timeout } = result } , opts , module ) do
op = CommandModules . module_to_command ( module )
2019-01-31 03:20:29 +08:00
2017-05-24 19:08:03 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
2022-10-03 01:54:11 +08:00
" Error: operation #{ op } on node #{ opts [ :node ] } timed out. Timeout value used: #{ opts [ :timeout ] } " }
2017-05-24 19:08:03 +08:00
end
2019-01-31 03:20:29 +08:00
2017-05-24 20:28:51 +08:00
defp format_error ( { :error , { :badrpc , { :timeout , to } } = result } , opts , module ) do
op = CommandModules . module_to_command ( module )
2019-01-31 03:20:29 +08:00
2017-05-24 20:28:51 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
2017-07-05 03:34:39 +08:00
" Error: operation #{ op } on node #{ opts [ :node ] } timed out. Timeout value used: #{ to } " }
2017-05-24 20:28:51 +08:00
end
2019-01-31 03:20:29 +08:00
2017-08-07 23:38:24 +08:00
defp format_error ( { :error , { :badrpc , { :timeout , to , warning } } } , opts , module ) do
op = CommandModules . module_to_command ( module )
2019-01-31 03:20:29 +08:00
2017-08-07 23:38:24 +08:00
{ :error , ExitCodes . exit_code_for ( { :timeout , to } ) ,
2022-10-03 01:54:11 +08:00
" Error: operation #{ op } on node #{ opts [ :node ] } timed out. Timeout value used: #{ to } . #{ warning } " }
2017-08-07 23:38:24 +08:00
end
2019-01-31 03:20:29 +08:00
2017-06-30 19:43:09 +08:00
defp format_error ( { :error , { :no_such_vhost , vhost } = result } , _opts , _ ) do
2019-01-31 03:20:29 +08:00
{ :error , ExitCodes . exit_code_for ( result ) , " Virtual host ' #{ vhost } ' does not exist " }
2017-06-30 19:44:35 +08:00
end
2019-01-31 03:20:29 +08:00
2023-05-02 17:28:29 +08:00
defp format_error ( { :error , { :not_found , vhost , name } = result } , _opts , _ ) do
{ :error , ExitCodes . exit_code_for ( result ) ,
2023-05-02 20:48:14 +08:00
" Object (queue, stream, exchange, etc) ' #{ name } ' was not found in virtual host ' #{ vhost } ' " }
2023-05-02 17:28:29 +08:00
end
defp format_error ( { :error , { :not_found , object_type , vhost , name } = result } , _opts , _ ) do
{ :error , ExitCodes . exit_code_for ( result ) ,
2023-05-02 20:48:14 +08:00
" #{ object_type } ' #{ name } ' was not found in virtual host ' #{ vhost } ' " }
2023-05-02 17:28:29 +08:00
end
2023-05-02 16:14:48 +08:00
defp format_error ( { :error , :not_found = result } , _opts , _ ) do
2023-05-02 17:08:31 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
" Object (queue, stream, exchange, etc) was not found " }
2023-05-02 16:14:48 +08:00
end
2022-10-03 01:54:11 +08:00
defp format_error (
{ :error , { :incompatible_version , local_version , remote_version } = result } ,
_opts ,
_
) do
{ :error , ExitCodes . exit_code_for ( result ) ,
" Detected potential version incompatibility. CLI tool version: #{ local_version } , server: #{ remote_version } " }
2019-04-18 23:14:02 +08:00
end
2017-06-09 01:15:21 +08:00
defp format_error ( { :error , { :timeout , to } = result } , opts , module ) do
op = CommandModules . module_to_command ( module )
2019-01-31 03:20:29 +08:00
2017-06-09 01:15:21 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
2017-07-05 03:34:39 +08:00
" Error: operation #{ op } on node #{ opts [ :node ] } timed out. Timeout value used: #{ to } " }
2017-06-09 01:15:21 +08:00
end
2019-01-31 03:20:29 +08:00
2017-06-09 01:15:21 +08:00
defp format_error ( { :error , :timeout = result } , opts , module ) do
op = CommandModules . module_to_command ( module )
2019-01-31 03:20:29 +08:00
2017-06-09 01:15:21 +08:00
{ :error , ExitCodes . exit_code_for ( result ) ,
2020-04-22 06:25:55 +08:00
" Error: operation #{ op } on node #{ opts [ :node ] } timed out. Timeout value used: #{ opts [ :timeout ] } " }
end
defp format_error ( { :error , :timeout , msg } , opts , module ) do
op = CommandModules . module_to_command ( module )
{ :error , ExitCodes . exit_code_for ( :timeout ) ,
" Error: operation #{ op } on node #{ opts [ :node ] } timed out: #{ msg } . Timeout value used: #{ opts [ :timeout ] } " }
2017-06-09 01:15:21 +08:00
end
2019-01-31 03:20:29 +08:00
2018-12-19 20:54:23 +08:00
# Plugins
defp format_error ( { :error , { :enabled_plugins_mismatch , cli_path , node_path } } , opts , _module ) do
{ :error , ExitCodes . exit_dataerr ( ) ,
2022-10-03 01:54:11 +08:00
" Could not update enabled plugins file at #{ cli_path } : target node #{ opts [ :node ] } uses a different path ( #{ node_path } ) " }
2018-12-19 20:54:23 +08:00
end
2019-01-31 03:20:29 +08:00
2018-12-19 20:54:23 +08:00
defp format_error ( { :error , { :cannot_read_enabled_plugins_file , path , :eacces } } , _opts , _module ) do
{ :error , ExitCodes . exit_dataerr ( ) ,
" Could not read enabled plugins file at #{ path } : the file does not exist or permission was denied (EACCES) " }
end
2019-01-31 03:20:29 +08:00
2018-12-19 20:54:23 +08:00
defp format_error ( { :error , { :cannot_read_enabled_plugins_file , path , :enoent } } , _opts , _module ) do
{ :error , ExitCodes . exit_dataerr ( ) ,
" Could not read enabled plugins file at #{ path } : the file does not exist (ENOENT) " }
end
2019-01-31 03:20:29 +08:00
2018-12-19 20:54:23 +08:00
defp format_error ( { :error , { :cannot_write_enabled_plugins_file , path , :eacces } } , _opts , _module ) do
{ :error , ExitCodes . exit_dataerr ( ) ,
" Could not update enabled plugins file at #{ path } : the file does not exist or permission was denied (EACCES) " }
end
2019-01-31 03:20:29 +08:00
2018-12-19 20:54:23 +08:00
defp format_error ( { :error , { :cannot_write_enabled_plugins_file , path , :enoent } } , _opts , _module ) do
{ :error , ExitCodes . exit_dataerr ( ) ,
" Could not update enabled plugins file at #{ path } : the file does not exist (ENOENT) " }
end
2019-01-31 03:20:29 +08:00
2019-01-22 12:43:25 +08:00
# Special case health checks. This makes it easier to change
# output of all health checks at once.
2019-05-29 20:59:59 +08:00
defp format_error ( { :error , :check_failed } , %{ formatter : " json " } , _ ) do
{ :error , ExitCodes . exit_unavailable ( ) , nil }
end
2022-10-03 01:54:11 +08:00
2019-05-29 20:59:59 +08:00
defp format_error ( { :error , :check_failed } , _ , _ ) do
{ :error , ExitCodes . exit_unavailable ( ) , nil }
end
2022-10-03 01:54:11 +08:00
2019-05-29 20:59:59 +08:00
defp format_error ( { :error , :check_failed , err } , %{ formatter : " json " } , _ ) when is_map ( err ) do
{ :ok , res } = JSON . encode ( err )
{ :error , ExitCodes . exit_unavailable ( ) , res }
end
2022-10-03 01:54:11 +08:00
2019-05-29 20:59:59 +08:00
defp format_error ( { :error , :check_failed , err } , %{ formatter : " json " } , _ ) do
{ :error , ExitCodes . exit_unavailable ( ) , err }
end
2022-10-03 01:54:11 +08:00
2019-12-05 16:15:24 +08:00
defp format_error ( { :error , :check_failed , err } , _ , _ ) do
{ :error , ExitCodes . exit_unavailable ( ) , err }
2019-01-22 12:43:25 +08:00
end
2019-01-31 03:20:29 +08:00
2019-01-22 12:43:25 +08:00
defp format_error ( { :error , nil } , _ , _ ) do
# the command intends to produce no output, e.g. a return code
# is sufficient
{ :error , ExitCodes . exit_unavailable ( ) , nil }
end
2019-01-31 03:20:29 +08:00
2019-11-19 09:39:29 +08:00
# Catch all clauses
2020-04-22 06:25:55 +08:00
defp format_error ( { :error , err } , %{ formatter : " json " } , _ ) when is_map ( err ) do
{ :ok , res } = JSON . encode ( err )
{ :error , ExitCodes . exit_unavailable ( ) , res }
end
2022-10-03 01:54:11 +08:00
2020-04-22 06:25:55 +08:00
defp format_error ( { :error , exit_code , err } , %{ formatter : " json " } , _ ) when is_map ( err ) do
{ :ok , res } = JSON . encode ( err )
{ :error , exit_code , res }
end
2019-01-23 15:44:19 +08:00
defp format_error ( { :error , exit_code , err } , _ , _ ) do
string_err = Helpers . string_or_inspect ( err )
{ :error , exit_code , " Error: \n #{ string_err } " }
end
2019-01-31 03:20:29 +08:00
2017-05-24 19:08:03 +08:00
defp format_error ( { :error , err } = result , _ , _ ) do
string_err = Helpers . string_or_inspect ( err )
2018-12-19 20:54:23 +08:00
2017-05-24 19:08:03 +08:00
{ :error , ExitCodes . exit_code_for ( result ) , " Error: \n #{ string_err } " }
end
2019-11-19 09:39:29 +08:00
defp format_error ( error , _opts , _module ) do
{ :error , ExitCodes . exit_software ( ) , " #{ inspect ( error ) } \n " }
end
2017-05-24 19:08:03 +08:00
defp get_node_diagnostics ( nil ) do
2017-07-05 03:34:39 +08:00
" Target node is not defined "
2017-05-24 19:08:03 +08:00
end
2019-01-31 03:20:29 +08:00
2017-05-24 19:08:03 +08:00
defp get_node_diagnostics ( node_name ) do
2017-07-05 03:34:39 +08:00
to_string ( :rabbit_nodes_common . diagnostics ( [ node_name ] ) )
end
defp badrpc_error_message_header ( node , _opts ) do
"""
Error : unable to perform an operation on node ' #{ node } ' . Please see diagnostics information and suggestions below .
Most common reasons for this are :
* Target node is unreachable ( e . g . due to hostname resolution , TCP connection or firewall issues )
2017-07-05 03:41:32 +08:00
* CLI tool fails to authenticate with the server ( e . g . due to CLI tool ' s Erlang cookie not matching that of the server)
* Target node is not running
2017-07-05 03:34:39 +08:00
In addition to the diagnostics info below :
2019-03-20 16:13:07 +08:00
* See the CLI , clustering and networking guides on https :/ / rabbitmq . com / documentation . html to learn more
2017-07-05 03:34:39 +08:00
* Consult server logs on node #{node}
2019-02-06 02:11:00 +08:00
* If target node is configured to use long node names , don ' t forget to use --longnames with CLI tools
2017-07-05 03:34:39 +08:00
"""
2017-05-24 19:08:03 +08:00
end
2017-12-07 02:10:11 +08:00
## Tries to enable erlang distribution, which can be configured
## via distribution callback in the command as :cli, :none or {:custom, fun()}.
## :cli - default rabbitmqctl node name
## :none - do not start a distribution (e.g. offline command)
2019-01-22 12:43:25 +08:00
## {:fun, fun} - run a custom function to enable distribution.
2017-12-07 02:10:11 +08:00
## custom mode is usefult for commands which should have specific node name.
## Runs code if distribution is successful, or not needed.
@spec maybe_with_distribution ( module ( ) , options ( ) , ( ( ) -> command_result ( ) ) ) :: command_result ( )
defp maybe_with_distribution ( command , options , code ) do
2019-04-25 22:15:25 +08:00
try do
maybe_with_distribution_without_catch ( command , options , code )
2022-10-03 01:54:11 +08:00
catch
error_type , error ->
maybe_print_stacktrace ( error_type , error , __STACKTRACE__ , options )
format_error ( error , options , command )
2019-04-25 22:15:25 +08:00
end
end
defp maybe_with_distribution_without_catch ( command , options , code ) do
2019-02-12 03:03:19 +08:00
distribution_type = CommandBehaviour . distribution ( command , options )
2019-01-31 03:20:29 +08:00
2017-12-07 02:10:11 +08:00
case distribution_type do
2019-01-31 03:20:29 +08:00
:none ->
code . ( )
:cli ->
2017-12-07 02:10:11 +08:00
case Distribution . start ( options ) do
2019-01-31 03:20:29 +08:00
:ok ->
2017-12-07 02:10:11 +08:00
code . ( )
2019-01-31 03:20:29 +08:00
2017-12-07 02:10:11 +08:00
{ :ok , _ } ->
code . ( )
2019-01-31 03:20:29 +08:00
2017-12-07 02:10:11 +08:00
{ :error , reason } ->
2019-01-31 03:20:29 +08:00
{ :error , ExitCodes . exit_config ( ) , " Distribution failed: #{ inspect ( reason ) } " }
2017-12-07 02:10:11 +08:00
end
2019-01-31 03:20:29 +08:00
2017-12-07 02:10:11 +08:00
{ :fun , fun } ->
case fun . ( options ) do
:ok ->
code . ( )
2019-01-31 03:20:29 +08:00
2017-12-07 02:10:11 +08:00
{ :error , reason } ->
2019-01-31 03:20:29 +08:00
{ :error , ExitCodes . exit_config ( ) , " Distribution failed: #{ inspect ( reason ) } " }
2017-12-07 02:10:11 +08:00
end
2016-12-05 22:49:56 +08:00
end
end
2016-02-03 02:54:36 +08:00
end