rabbitmq-server/deps/rabbitmq_cli/lib/rabbitmqctl.ex

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

672 lines
22 KiB
Elixir
Raw Normal View History

## 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
alias RabbitMQ.CLI.{CommandBehaviour, FormatterBehaviour}
2019-01-31 03:20:29 +08:00
alias RabbitMQ.CLI.Ctl.Commands.HelpCommand
# Enable unit tests for private functions
2019-01-31 03:20:29 +08:00
@compile if Mix.env() == :test, do: :export_all
@type options() :: map()
2019-01-31 03:20:29 +08:00
@type command_result() :: {:error, ExitCodes.exit_code(), term()} | term()
@spec main(list()) :: no_return()
def main(["--auto-complete" | []]) do
handle_shutdown(:ok)
end
2019-01-31 03:20:29 +08:00
def main(unparsed_command) do
exec_command(unparsed_command, &process_output/3)
2016-12-23 20:12:33 +08:00
|> handle_shutdown
end
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), "")}
end
2022-10-03 01:54:11 +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), "")}
end
2022-10-03 01:54:11 +08:00
def exec_command(["--version"] = _unparsed_command, output_fun) do
2019-02-14 20:10:22 +08:00
# rewrite `--version` as `version`
exec_command(["version"], output_fun)
2019-02-14 20:10:22 +08:00
end
2022-10-03 01:54:11 +08:00
def exec_command(["--auto-complete" | args], output_fun) do
# rewrite `--auto-complete` as `autocomplete`
exec_command(["autocomplete" | args], output_fun)
end
2022-10-03 01:54:11 +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)
case {command, invalid} do
{:no_command, _} ->
2019-01-31 03:20:29 +08:00
command_not_found_string =
case command_name do
"" -> ""
_ -> "\nCommand '#{command_name}' not found. \n"
end
usage_string =
command_not_found_string <>
Enum.join(HelpCommand.all_usage(parsed_options), "")
2019-01-31 03:20:29 +08:00
{:error, ExitCodes.exit_usage(), usage_string}
{{:suggest, suggested}, _} ->
2019-01-31 03:20:29 +08:00
suggest_message =
"\nCommand '#{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
_ ->
options = parsed_options |> merge_all_defaults |> normalise_options
2019-01-31 03:20:29 +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)
end
end
end
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)}
_ ->
{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
{: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
:ok ->
result = proceed_to_execution(command, arguments, options)
handle_command_output(result, command, options, output_fun)
{:validation_failure, err} ->
environment_validation_error_output(err, command, unparsed_command, options)
{:error, _} = err ->
format_error(err, options, command)
end
{:validation_failure, err} ->
argument_validation_error_output(err, command, unparsed_command, options)
{:error, _} = err ->
format_error(err, options, command)
end
end
end)
2016-11-04 02:10:18 +08:00
end
end
2016-06-08 22:19:37 +08:00
defp proceed_to_execution(command, arguments, options) do
_ = maybe_print_banner(command, arguments, options)
maybe_run_command(command, arguments, options)
end
defp maybe_run_command(_, _, %{dry_run: true}) do
:ok
end
2019-01-31 03:20:29 +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 ->
maybe_print_stacktrace(error_type, error, __STACKTRACE__, options)
2019-01-31 03:20:29 +08:00
format_error(error, options, command)
end
end
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
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
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
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
def maybe_print_stacktrace(error_type, error, stacktrace, %{print_stacktrace: true}) do
do_print_stacktrace(error_type, error, stacktrace)
end
2022-10-03 01:54:11 +08:00
def maybe_print_stacktrace(_error_type, _error, _stacktrace, %{print_stacktrace: false}) do
nil
end
2022-10-03 01:54:11 +08:00
def maybe_print_stacktrace(_error_type, _error, _stacktrace, _opts) do
nil
end
defp do_print_stacktrace(error_type, error, stacktrace) do
IO.puts("Stack trace: \n")
IO.puts(Exception.format(error_type, error, stacktrace))
end
def handle_command_output(output, command, options, output_fun) do
case output do
{:error, _, _} = err ->
format_error(err, options, command)
2019-01-31 03:20:29 +08:00
{:error, _} = err ->
2019-01-31 03:20:29 +08:00
format_error(err, options, command)
_ ->
output_fun.(output, command, options)
end
end
defp process_output(output, command, options) do
formatter = Config.get_formatter(command, options)
printer = Config.get_printer(command, options)
output
|> Output.format_output(formatter, options)
|> Output.print_output(printer, options)
|> case do
2019-01-31 03:20:29 +08:00
{:error, _} = err -> format_error(err, options, command)
other -> other
end
end
defp output_device(exit_code) do
2019-01-31 03:20:29 +08:00
case exit_code == ExitCodes.exit_ok() do
true -> :stdio
false -> :stderr
end
end
@spec handle_shutdown({:error, integer(), nil} | atom()) :: no_return()
defp handle_shutdown({:error, exit_code, nil}) do
exit_program(exit_code)
end
2019-01-31 03:20:29 +08:00
defp handle_shutdown({_, exit_code, output}) do
device = output_device(exit_code)
2017-04-05 02:23:50 +08:00
Enum.each(List.flatten([output]), fn line ->
IO.puts(device, Helpers.string_or_inspect(line))
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
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
def merge_all_defaults(%{} = options) do
options
2018-12-01 06:48:47 +08:00
|> merge_defaults_node
|> merge_defaults_timeout
2016-06-08 22:19:37 +08:00
|> merge_defaults_longnames
end
defp merge_defaults_node(%{} = opts) do
longnames_opt = Config.get_option(:longnames, opts)
2022-10-03 01:54:11 +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
end
end
2018-12-01 06:48:47 +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)
defp normalise_options(opts) do
opts |> normalise_timeout
end
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
defp normalise_timeout(opts) do
2016-11-11 01:40:43 +08:00
opts
end
# 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
# 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
# Suppress banner if a machine-readable formatter is used
formatter = Map.get(opts, :formatter)
2022-10-03 01:54:11 +08:00
case FormatterBehaviour.machine_readable?(formatter) do
2022-10-03 01:54:11 +08:00
true ->
nil
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
end
end
def argument_validation_error_output(err_detail, command, unparsed_command, options) do
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}\nArguments given:\n\t#{unparsed_command |> Enum.join(" ")}"
2019-01-31 03:20:29 +08:00
validation_error_output(err_detail, base_error, command, options)
end
def environment_validation_error_output(err_detail, command, unparsed_command, options) do
err = format_validation_error(err_detail)
base_error = "Error: #{err}\nArguments 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)
message = base_error <> "\n" <> usage
{:error, ExitCodes.exit_code_for({:validation_failure, err_detail}), message}
end
2016-12-23 20:12:33 +08:00
defp format_validation_error(:not_enough_args), do: "not enough arguments."
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."
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."
defp format_validation_error({:bad_argument, detail}), do: "Bad argument: #{detail}"
2019-01-31 03:20:29 +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"
)
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."
defp format_validation_error(:unsupported_formatter),
do: "the requested formatter is not supported by this command"
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)
@spec exit_program(integer()) :: no_return()
defp exit_program(code) do
_ = :net_kernel.stop()
exit({:shutdown, code})
end
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).\nIf using FQDN node names, use the -l / --longnames argument."}
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
{: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.\nIf using FQDN node names, use the -l / --longnames argument"}
end
2022-10-03 01:54:11 +08:00
defp format_error({:error, {:node_name, err_reason} = result}, opts, module) do
op = CommandModules.module_to_command(module)
node = opts[:node] || "(failed to parse or compute default value)"
2022-10-03 01:54:11 +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}).\nIf using FQDN node names, use the -l / --longnames argument"}
end
defp format_error({:error, {:badrpc_multi, :nodedown, [node | _]} = result}, opts, _) do
diagnostics = get_node_diagnostics(node)
2019-01-31 03:20:29 +08:00
{:error, ExitCodes.exit_code_for(result),
badrpc_error_message_header(node, opts) <> diagnostics}
end
2019-01-31 03:20:29 +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
{:error, ExitCodes.exit_code_for(result),
"Error: operation #{op} on node #{node} timed out. Timeout value used: #{opts[:timeout]}"}
end
2019-01-31 03:20:29 +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
{:error, ExitCodes.exit_code_for(result),
badrpc_error_message_header(opts[:node], opts) <> diagnostics}
end
2019-01-31 03:20:29 +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
{: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]}"}
end
2019-01-31 03:20:29 +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
{:error, ExitCodes.exit_code_for(result),
"Error: operation #{op} on node #{opts[:node]} timed out. Timeout value used: #{to}"}
end
2019-01-31 03:20:29 +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
{: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}"}
end
2019-01-31 03:20:29 +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
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}'"}
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}'"}
end
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"}
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}"}
end
defp format_error({:error, {:timeout, to} = result}, opts, module) do
op = CommandModules.module_to_command(module)
2019-01-31 03:20:29 +08:00
{:error, ExitCodes.exit_code_for(result),
"Error: operation #{op} on node #{opts[:node]} timed out. Timeout value used: #{to}"}
end
2019-01-31 03:20:29 +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
{:error, ExitCodes.exit_code_for(result),
"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]}"}
end
2019-01-31 03:20:29 +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})"}
end
2019-01-31 03:20:29 +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
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
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
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
# Special case health checks. This makes it easier to change
# output of all health checks at once.
defp format_error({:error, :check_failed}, %{formatter: "json"}, _) do
{:error, ExitCodes.exit_unavailable(), nil}
end
2022-10-03 01:54:11 +08:00
defp format_error({:error, :check_failed}, _, _) do
{:error, ExitCodes.exit_unavailable(), nil}
end
2022-10-03 01:54:11 +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
defp format_error({:error, :check_failed, err}, %{formatter: "json"}, _) do
{:error, ExitCodes.exit_unavailable(), err}
end
2022-10-03 01:54:11 +08:00
defp format_error({:error, :check_failed, err}, _, _) do
{:error, ExitCodes.exit_unavailable(), err}
end
2019-01-31 03:20:29 +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
# Catch all clauses
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
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
defp format_error({:error, err} = result, _, _) do
string_err = Helpers.string_or_inspect(err)
{:error, ExitCodes.exit_code_for(result), "Error:\n#{string_err}"}
end
defp format_error(error, _opts, _module) do
{:error, ExitCodes.exit_software(), "#{inspect(error)}\n"}
end
defp get_node_diagnostics(nil) do
"Target node is not defined"
end
2019-01-31 03:20:29 +08:00
defp get_node_diagnostics(node_name) do
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
In addition to the diagnostics info below:
URL Cleanup This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener). # HTTP URLs that Could Not Be Fixed These URLs were unable to be fixed. Please review them to see if they can be manually resolved. * http://blog.listincomprehension.com/search/label/procket (200) with 1 occurrences could not be migrated: ([https](https://blog.listincomprehension.com/search/label/procket) result ClosedChannelException). * http://dozzie.jarowit.net/trac/wiki/TOML (200) with 1 occurrences could not be migrated: ([https](https://dozzie.jarowit.net/trac/wiki/TOML) result SSLHandshakeException). * http://dozzie.jarowit.net/trac/wiki/subproc (200) with 1 occurrences could not be migrated: ([https](https://dozzie.jarowit.net/trac/wiki/subproc) result SSLHandshakeException). * http://e2project.org (200) with 1 occurrences could not be migrated: ([https](https://e2project.org) result AnnotatedConnectException). * http://erlang.org/doc/reference_manual/distributed.html (200) with 1 occurrences could not be migrated: ([https](https://erlang.org/doc/reference_manual/distributed.html) result ConnectTimeoutException). * http://nitrogenproject.com/ (200) with 2 occurrences could not be migrated: ([https](https://nitrogenproject.com/) result ConnectTimeoutException). * http://proper.softlab.ntua.gr (200) with 1 occurrences could not be migrated: ([https](https://proper.softlab.ntua.gr) result SSLHandshakeException). * http://yaws.hyber.org (200) with 1 occurrences could not be migrated: ([https](https://yaws.hyber.org) result AnnotatedConnectException). * http://choven.ca (503) with 1 occurrences could not be migrated: ([https](https://choven.ca) result ConnectTimeoutException). # Fixed URLs ## Fixed But Review Recommended These URLs were fixed, but the https status was not OK. However, the https status was the same as the http request or http redirected to an https URL, so they were migrated. Your review is recommended. * http://fixprotocol.org/ (301) with 1 occurrences migrated to: https://fixtrading.org ([https](https://fixprotocol.org/) result SSLHandshakeException). * http://erldb.org (UnknownHostException) with 1 occurrences migrated to: https://erldb.org ([https](https://erldb.org) result UnknownHostException). * http://elixir-lang.org/docs/stable/elixir/OptionParser.html (301) with 1 occurrences migrated to: https://elixir-lang.org/docs/stable/elixir/OptionParser.html ([https](https://elixir-lang.org/docs/stable/elixir/OptionParser.html) result 404). * http://elixir-lang.org/docs/stable/elixir/Stream.html (301) with 1 occurrences migrated to: https://elixir-lang.org/docs/stable/elixir/Stream.html ([https](https://elixir-lang.org/docs/stable/elixir/Stream.html) result 404). ## Fixed Success These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended. * http://cloudi.org/ with 27 occurrences migrated to: https://cloudi.org/ ([https](https://cloudi.org/) result 200). * http://elixir-lang.org with 1 occurrences migrated to: https://elixir-lang.org ([https](https://elixir-lang.org) result 200). * http://elixir-lang.org/ with 2 occurrences migrated to: https://elixir-lang.org/ ([https](https://elixir-lang.org/) result 200). * http://elixir-lang.org/getting-started/typespecs-and-behaviours.html with 1 occurrences migrated to: https://elixir-lang.org/getting-started/typespecs-and-behaviours.html ([https](https://elixir-lang.org/getting-started/typespecs-and-behaviours.html) result 200). * http://elixir-lang.org/install.html with 1 occurrences migrated to: https://elixir-lang.org/install.html ([https](https://elixir-lang.org/install.html) result 200). * http://erlware.org/ with 1 occurrences migrated to: https://erlware.org/ ([https](https://erlware.org/) result 200). * http://inaka.github.io/cowboy-trails/ with 1 occurrences migrated to: https://inaka.github.io/cowboy-trails/ ([https](https://inaka.github.io/cowboy-trails/) result 200). * http://ninenines.eu with 6 occurrences migrated to: https://ninenines.eu ([https](https://ninenines.eu) result 200). * http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html with 1 occurrences migrated to: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html ([https](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) result 200). * http://www.actordb.com/ with 2 occurrences migrated to: https://www.actordb.com/ ([https](https://www.actordb.com/) result 200). * http://www.cs.kent.ac.uk/projects/wrangler/Home.html with 1 occurrences migrated to: https://www.cs.kent.ac.uk/projects/wrangler/Home.html ([https](https://www.cs.kent.ac.uk/projects/wrangler/Home.html) result 200). * http://www.rebar3.org with 1 occurrences migrated to: https://www.rebar3.org ([https](https://www.rebar3.org) result 200). * http://contributor-covenant.org with 1 occurrences migrated to: https://contributor-covenant.org ([https](https://contributor-covenant.org) result 301). * http://contributor-covenant.org/version/1/3/0/ with 1 occurrences migrated to: https://contributor-covenant.org/version/1/3/0/ ([https](https://contributor-covenant.org/version/1/3/0/) result 301). * http://inaka.github.com/apns4erl with 1 occurrences migrated to: https://inaka.github.com/apns4erl ([https](https://inaka.github.com/apns4erl) result 301). * http://inaka.github.com/edis/ with 1 occurrences migrated to: https://inaka.github.com/edis/ ([https](https://inaka.github.com/edis/) result 301). * http://lasp-lang.org/ with 1 occurrences migrated to: https://lasp-lang.org/ ([https](https://lasp-lang.org/) result 301). * http://rabbitmq.com/documentation.html with 1 occurrences migrated to: https://rabbitmq.com/documentation.html ([https](https://rabbitmq.com/documentation.html) result 301). * http://saleyn.github.com/erlexec with 1 occurrences migrated to: https://saleyn.github.com/erlexec ([https](https://saleyn.github.com/erlexec) result 301). * http://www.mozilla.org/MPL/ with 290 occurrences migrated to: https://www.mozilla.org/MPL/ ([https](https://www.mozilla.org/MPL/) result 301). * http://zhongwencool.github.io/observer_cli with 1 occurrences migrated to: https://zhongwencool.github.io/observer_cli ([https](https://zhongwencool.github.io/observer_cli) result 301).
2019-03-20 16:13:07 +08:00
* See the CLI, clustering and networking guides on https://rabbitmq.com/documentation.html to learn more
* Consult server logs on node #{node}
* If target node is configured to use long node names, don't forget to use --longnames with CLI tools
"""
end
## 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)
## {:fun, fun} - run a custom function to enable distribution.
## 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
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)
end
end
defp maybe_with_distribution_without_catch(command, options, code) do
distribution_type = CommandBehaviour.distribution(command, options)
2019-01-31 03:20:29 +08:00
case distribution_type do
2019-01-31 03:20:29 +08:00
:none ->
code.()
:cli ->
case Distribution.start(options) do
2019-01-31 03:20:29 +08:00
:ok ->
code.()
2019-01-31 03:20:29 +08:00
{:ok, _} ->
code.()
2019-01-31 03:20:29 +08:00
{:error, reason} ->
2019-01-31 03:20:29 +08:00
{:error, ExitCodes.exit_config(), "Distribution failed: #{inspect(reason)}"}
end
2019-01-31 03:20:29 +08:00
{:fun, fun} ->
case fun.(options) do
:ok ->
code.()
2019-01-31 03:20:29 +08:00
{:error, reason} ->
2019-01-31 03:20:29 +08:00
{:error, ExitCodes.exit_config(), "Distribution failed: #{inspect(reason)}"}
end
end
end
2016-02-03 02:54:36 +08:00
end