Merge pull request #100 from rabbitmq/rabbitmq-cli-90
Command error and output reporting framework
This commit is contained in:
commit
e1e79f6e46
|
|
@ -23,4 +23,7 @@ defmodule RabbitMQ.CLI.CommandBehaviour do
|
|||
@callback run(List.t, Map.t) :: any
|
||||
@callback switches() :: Keyword.t
|
||||
@callback aliases() :: Keyword.t
|
||||
# Coerces run/2 return value into the standard command output form
|
||||
# that is then formatted, printed and returned as an exit code.
|
||||
@callback output(any, Map.t) :: :ok | {:ok, any} | {:stream, Enum.t} | {:error, ExitCodes.exit_code, [String.t]}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
@flags []
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.AddVhostCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def validate([], _), do: {:validation_failure, :not_enough_args}
|
||||
|
|
|
|||
|
|
@ -38,4 +38,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AuthenticateUserCommand do
|
|||
def banner([username, _password], _), do: "Authenticating user \"#{username}\" ..."
|
||||
|
||||
def flags, do: @flags
|
||||
|
||||
def output({:refused, user, msg, args}, _) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_dataerr,
|
||||
"Error: failed to authenticate user \"#{user}\"\n" <>
|
||||
to_string(:io_lib.format(msg, args))}
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.CancelSyncQueueCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def merge_defaults([_|_] = args, opts) do
|
||||
{args, Map.merge(default_opts, opts)}
|
||||
|
|
|
|||
|
|
@ -36,22 +36,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ChangeClusterNodeTypeCommand do
|
|||
def validate(_, _), do: {:validation_failure, :too_many_args}
|
||||
|
||||
def run([node_type_arg], %{node: node_name}) do
|
||||
ret = case normalize_type(String.to_atom(node_type_arg)) do
|
||||
:ram ->
|
||||
:rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_mnesia, :change_cluster_node_type, [:ram]
|
||||
);
|
||||
:disc ->
|
||||
:rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_mnesia, :change_cluster_node_type, [:disc]
|
||||
)
|
||||
end
|
||||
|
||||
case ret do
|
||||
{:error, reason} ->
|
||||
{:change_node_type_failed, {reason, node_name}}
|
||||
result ->
|
||||
result
|
||||
case normalize_type(String.to_atom(node_type_arg)) do
|
||||
:ram ->
|
||||
:rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_mnesia, :change_cluster_node_type, [:ram]
|
||||
);
|
||||
:disc ->
|
||||
:rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_mnesia, :change_cluster_node_type, [:disc])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -63,6 +55,12 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ChangeClusterNodeTypeCommand do
|
|||
"Turning #{node_name} into a #{node_type} node"
|
||||
end
|
||||
|
||||
def output({:error, :mnesia_unexpectedly_running}, %{node: node_name}) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_software,
|
||||
RabbitMQ.CLI.DefaultOutput.mnesia_running_error(node_name)}
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
defp normalize_type(:ram) do
|
||||
:ram
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ChangePasswordCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ClearParameterCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def switches(), do: []
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ClearPasswordCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def validate([], _), do: {:validation_failure, :not_enough_args}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ClearPermissionsCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ClearPolicyCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def switches(), do: []
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.CloseConnectionCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ClusterStatusCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.DeleteUserCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def validate([], _), do: {:validation_failure, :not_enough_args}
|
||||
def validate(args, _) when length(args) > 1, do: {:validation_failure, :too_many_args}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.DeleteVhostCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def validate([], _), do: {:validation_failure, :not_enough_args}
|
||||
def validate([_|_] = args, _) when length(args) > 1, do: {:validation_failure, :too_many_args}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.EnvironmentCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def validate([_|_], _), do: {:validation_failure, :too_many_args}
|
||||
def validate(_, _), do: :ok
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.EvalCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ForceBootCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,21 +16,18 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ForceResetCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
def validate([_|_] = args, _) when length(args) > 0, do: {:validation_failure, :too_many_args}
|
||||
def validate([_|_] = args, _) when length(args) > 0, do: {:validation_failure, :too_many_args}
|
||||
def validate([], _), do: :ok
|
||||
def switches(), do: []
|
||||
def aliases(), do: []
|
||||
|
||||
|
||||
def run([], %{node: node_name}) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_mnesia, :force_reset, []) do
|
||||
{:error, reason} ->
|
||||
{:reset_failed, {reason, node_name}}
|
||||
result -> result
|
||||
end
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_mnesia, :force_reset, [])
|
||||
end
|
||||
|
||||
def usage, do: "force_reset"
|
||||
|
|
@ -38,4 +35,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForceResetCommand do
|
|||
def flags, do: @flags
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Forcefully resetting node #{node_name} ..."
|
||||
|
||||
def output({:error, :mnesia_unexpectedly_running}, %{node: node_name}) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_software,
|
||||
RabbitMQ.CLI.DefaultOutput.mnesia_running_error(node_name)}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForgetClusterNodeCommand do
|
|||
import RabbitMQ.CLI.Coerce
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def flags, do: [:offline]
|
||||
def switches(), do: [offline: :boolean]
|
||||
|
|
@ -42,9 +43,12 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForgetClusterNodeCommand do
|
|||
end
|
||||
|
||||
def run([node_to_remove], %{node: node_name, offline: true}) do
|
||||
become(node_name)
|
||||
:rabbit_event.start_link()
|
||||
:rabbit_mnesia.forget_cluster_node(to_atom(node_to_remove), true)
|
||||
Stream.concat([
|
||||
become(node_name),
|
||||
RabbitMQ.CLI.Ctl.Helpers.defer(fn() ->
|
||||
:rabbit_event.start_link()
|
||||
:rabbit_mnesia.forget_cluster_node(to_atom(node_to_remove), true)
|
||||
end)])
|
||||
end
|
||||
|
||||
def run([node_to_remove], %{node: node_name, offline: false}) do
|
||||
|
|
@ -66,12 +70,17 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForgetClusterNodeCommand do
|
|||
:error_logger.tty(false)
|
||||
case :net_adm.ping(node_name) do
|
||||
:pong -> exit({:node_running, node_name});
|
||||
:pang -> ok = :net_kernel.stop()
|
||||
IO.puts(" * Impersonating node: #{node_name}...")
|
||||
{:ok, _} = Distribution.start_as(node_name)
|
||||
IO.puts(" done")
|
||||
dir = :mnesia.system_info(:directory)
|
||||
IO.puts(" * Mnesia directory: #{dir}...")
|
||||
:pang -> :ok = :net_kernel.stop()
|
||||
Stream.concat([
|
||||
[" * Impersonating node: #{node_name}..."],
|
||||
RabbitMQ.CLI.Ctl.Helpers.defer(fn() ->
|
||||
{:ok, _} = Distribution.start_as(node_name)
|
||||
" done"
|
||||
end),
|
||||
RabbitMQ.CLI.Ctl.Helpers.defer(fn() ->
|
||||
dir = :mnesia.system_info(:directory)
|
||||
" * Mnesia directory: #{dir}..."
|
||||
end)])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
alias RabbitMQ.CLI.ExitCodes, as: ExitCodes
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
# use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def validate(_, _), do: :ok
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
@ -31,57 +32,60 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
case Helpers.is_command?(command_name) do
|
||||
true ->
|
||||
command = Helpers.commands[command_name]
|
||||
print_base_usage(program_name(), command)
|
||||
print_options_usage
|
||||
print_input_types(command);
|
||||
Enum.join([base_usage(program_name(), command)] ++
|
||||
options_usage ++
|
||||
input_types(command), "\n");
|
||||
false ->
|
||||
all_usage()
|
||||
ExitCodes.exit_usage
|
||||
end
|
||||
end
|
||||
|
||||
def run(_, _) do
|
||||
all_usage()
|
||||
end
|
||||
|
||||
# Help command should always exit with usage
|
||||
def output(result, _) do
|
||||
{:error, ExitCodes.exit_usage, result}
|
||||
end
|
||||
|
||||
def program_name() do
|
||||
String.to_atom(Path.basename(:escript.script_name()))
|
||||
end
|
||||
|
||||
def all_usage() do
|
||||
print_base_usage(program_name())
|
||||
print_options_usage
|
||||
print_commands
|
||||
print_input_types
|
||||
:ok
|
||||
Enum.join(base_usage(program_name()) ++
|
||||
options_usage ++
|
||||
commands ++
|
||||
input_types, "\n")
|
||||
end
|
||||
|
||||
def usage(), do: "help <command>"
|
||||
|
||||
defp print_base_usage(tool_name = :'rabbitmqctl') do
|
||||
IO.puts "Usage:"
|
||||
IO.puts "#{tool_name} [-n <node>] [-t <timeout>] [-l] [-q] <command> [<command options>]"
|
||||
defp base_usage(tool_name = :'rabbitmqctl') do
|
||||
["Usage:",
|
||||
"#{tool_name} [-n <node>] [-t <timeout>] [-l] [-q] <command> [<command options>]"]
|
||||
|
||||
end
|
||||
|
||||
defp print_base_usage(tool_name = :'rabbitmq-plugins') do
|
||||
IO.puts "Usage:"
|
||||
IO.puts "#{tool_name} [-n <node>] [-q] <command> [<command options>]"
|
||||
defp base_usage(tool_name = :'rabbitmq-plugins') do
|
||||
["Usage:",
|
||||
"#{tool_name} [-n <node>] [-q] <command> [<command options>]"]
|
||||
end
|
||||
|
||||
defp print_base_usage(tool_name = :'rabbitmq_plugins') do
|
||||
IO.puts "Usage:"
|
||||
IO.puts "#{tool_name} [-n <node>] [-q] <command> [<command options>]"
|
||||
defp base_usage(tool_name = :'rabbitmq_plugins') do
|
||||
["Usage:",
|
||||
"#{tool_name} [-n <node>] [-q] <command> [<command options>]"]
|
||||
end
|
||||
|
||||
defp print_base_usage(tool_name) do
|
||||
IO.puts "Usage:"
|
||||
IO.puts "#{tool_name} [-n <node>] [-q] <command> [<command options>]"
|
||||
defp base_usage(tool_name) do
|
||||
["Usage:",
|
||||
"#{tool_name} [-n <node>] [-q] <command> [<command options>]"]
|
||||
end
|
||||
|
||||
def print_base_usage(tool_name, command) do
|
||||
IO.puts "Usage:"
|
||||
IO.puts "#{tool_name} [-n <node>] [-t <timeout>] [-q] " <>
|
||||
flatten_string(command.usage())
|
||||
def base_usage(tool_name, command) do
|
||||
Enum.join(["Usage:",
|
||||
"#{tool_name} [-n <node>] [-t <timeout>] [-q] " <>
|
||||
flatten_string(command.usage())], "\n")
|
||||
end
|
||||
|
||||
defp flatten_string(list) when is_list(list) do
|
||||
|
|
@ -91,8 +95,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
str
|
||||
end
|
||||
|
||||
defp print_options_usage() do
|
||||
IO.puts "
|
||||
defp options_usage() do
|
||||
["
|
||||
General options:
|
||||
-n node
|
||||
-q
|
||||
|
|
@ -116,40 +120,36 @@ If RabbitMQ broker uses long node names for erlang distribution, \"longnames\"
|
|||
option should be specified.
|
||||
|
||||
Some commands accept an optional virtual host parameter for which
|
||||
to display results. The default value is \"/\".\n"
|
||||
to display results. The default value is \"/\".\n"]
|
||||
end
|
||||
|
||||
defp print_commands() do
|
||||
IO.puts "Commands:"
|
||||
|
||||
# Enum.map obtains the usage string for each command module.
|
||||
# Enum.each prints them all.
|
||||
Helpers.commands
|
||||
|> Map.values
|
||||
|> Enum.sort
|
||||
|> Enum.map(&(&1.usage))
|
||||
|> List.flatten
|
||||
|> Enum.each(fn(cmd_usage) -> IO.puts " #{cmd_usage}" end)
|
||||
|
||||
:ok
|
||||
defp commands() do
|
||||
["Commands:" |
|
||||
# Enum.map obtains the usage string for each command module.
|
||||
# Enum.each prints them all.
|
||||
Helpers.commands
|
||||
|> Map.values
|
||||
|> Enum.sort
|
||||
|> Enum.map(&(&1.usage))
|
||||
|> List.flatten
|
||||
|> Enum.map(fn(cmd_usage) -> " #{cmd_usage}" end)]
|
||||
end
|
||||
|
||||
defp print_input_types(command) do
|
||||
defp input_types(command) do
|
||||
if :erlang.function_exported(command, :usage_additional, 0) do
|
||||
IO.puts(command.usage_additional())
|
||||
[command.usage_additional()]
|
||||
else
|
||||
:ok
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp print_input_types() do
|
||||
Helpers.commands
|
||||
|> Map.values
|
||||
|> Enum.filter_map(
|
||||
&:erlang.function_exported(&1, :usage_additional, 0),
|
||||
&(&1.usage_additional))
|
||||
|> Enum.join("\n\n")
|
||||
|> IO.puts
|
||||
defp input_types() do
|
||||
[Helpers.commands
|
||||
|> Map.values
|
||||
|> Enum.filter_map(
|
||||
&:erlang.function_exported(&1, :usage_additional, 0),
|
||||
&(&1.usage_additional))
|
||||
|> Enum.join("\n\n")]
|
||||
end
|
||||
|
||||
def banner(_,_), do: nil
|
||||
|
|
|
|||
|
|
@ -50,17 +50,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.JoinClusterCommand do
|
|||
true -> :ram
|
||||
_ -> :disc
|
||||
end
|
||||
ret = :rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_mnesia,
|
||||
:join_cluster,
|
||||
[Helpers.parse_node(target_node), node_type]
|
||||
)
|
||||
case ret do
|
||||
{:error, reason} ->
|
||||
{:join_cluster_failed, {reason, node_name}}
|
||||
result ->
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def usage() do
|
||||
|
|
@ -70,4 +64,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.JoinClusterCommand do
|
|||
def banner([target_node], %{node: node_name}) do
|
||||
"Clustering node #{node_name} with #{target_node}"
|
||||
end
|
||||
|
||||
def output({:error, :mnesia_unexpectedly_running}, %{node: node_name}) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_software,
|
||||
RabbitMQ.CLI.DefaultOutput.mnesia_running_error(node_name)}
|
||||
end
|
||||
def output({:error, :cannot_cluster_node_with_itself}, %{node: node_name}) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_software,
|
||||
"Error: cannot cluster node with itself: #{node_name}"}
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListBindingsCommand do
|
|||
alias RabbitMQ.CLI.Ctl.RpcStream, as: RpcStream
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
@info_keys ~w(source_name source_kind destination_name destination_kind routing_key arguments)a
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListChannelsCommand do
|
|||
alias RabbitMQ.CLI.Ctl.InfoKeys, as: InfoKeys
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
@info_keys ~w(pid connection name number user vhost transactional
|
||||
confirm consumer_count messages_unacknowledged
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListConnectionsCommand do
|
|||
alias RabbitMQ.CLI.Ctl.RpcStream, as: RpcStream
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
@info_keys ~w(pid name port host peer_port peer_host ssl ssl_protocol
|
||||
ssl_key_exchange ssl_cipher ssl_hash peer_cert_subject
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListConsumersCommand do
|
|||
alias RabbitMQ.CLI.Ctl.RpcStream, as: RpcStream
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
@info_keys ~w(queue_name channel_pid consumer_tag
|
||||
ack_required prefetch_count arguments)a
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListExchangesCommand do
|
|||
alias RabbitMQ.CLI.Ctl.RpcStream, as: RpcStream
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
@info_keys ~w(name type durable auto_delete internal arguments policy)a
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ListParametersCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
def merge_defaults([], opts) do
|
||||
{[], Map.merge(%{vhost: "/"}, opts)}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ListPermissionsCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ListPoliciesCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def switches(), do: []
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListQueuesCommand do
|
|||
alias RabbitMQ.CLI.Ctl.Helpers, as: Helpers
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
@info_keys ~w(name durable auto_delete
|
||||
arguments policy pid owner_pid exclusive exclusive_consumer_pid
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ListUserPermissionsCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def validate([], _), do: {:validation_failure, :not_enough_args}
|
||||
def validate([_|_] = args, _) when length(args) > 1, do: {:validation_failure, :too_many_args}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ListUsersCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListVhostsCommand do
|
|||
alias RabbitMQ.CLI.Ctl.InfoKeys, as: InfoKeys
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
@info_keys ~w(name tracing)a
|
||||
|
||||
|
|
|
|||
|
|
@ -55,4 +55,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.NodeHealthCheckCommand do
|
|||
other
|
||||
end
|
||||
end
|
||||
|
||||
def output({:healthcheck_failed, message}, _) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_software,
|
||||
"Error: healthcheck failed. Message: #{message}"}
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.PurgeQueueCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def flags, do: []
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.RenameClusterNodeCommand do
|
|||
import RabbitMQ.CLI.Coerce
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def flags, do: [:mnesia_dir, :rabbitmq_home]
|
||||
def switches(), do: [mnesia_dir: :string, rabbitmq_home: :string]
|
||||
|
|
@ -56,7 +57,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.RenameClusterNodeCommand do
|
|||
try do
|
||||
:rabbit_mnesia_rename.rename(node_name, node_pairs)
|
||||
catch _, reason ->
|
||||
{:error, reason}
|
||||
{:rename_failed, reason}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ReportCommand do
|
|||
alias RabbitMQ.CLI.Ctl.Commands.ListPermissionsCommand, as: ListPermissionsCommand
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def switches(), do: []
|
||||
|
|
|
|||
|
|
@ -19,18 +19,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ResetCommand do
|
|||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
def validate([_|_] = args, _) when length(args) > 0, do: {:validation_failure, :too_many_args}
|
||||
def validate([_|_] = args, _) when length(args) > 0, do: {:validation_failure, :too_many_args}
|
||||
def validate([], _), do: :ok
|
||||
def switches(), do: []
|
||||
def aliases(), do: []
|
||||
|
||||
|
||||
def run([], %{node: node_name}) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_mnesia, :reset, []) do
|
||||
{:error, reason} ->
|
||||
{:reset_failed, {reason, node_name}}
|
||||
result -> result
|
||||
end
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_mnesia, :reset, [])
|
||||
end
|
||||
|
||||
def usage, do: "reset"
|
||||
|
|
@ -38,4 +34,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ResetCommand do
|
|||
def flags, do: @flags
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Resetting node #{node_name} ..."
|
||||
|
||||
|
||||
def output({:error, :mnesia_unexpectedly_running}, %{node: node_name}) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_software,
|
||||
RabbitMQ.CLI.DefaultOutput.mnesia_running_error(node_name)}
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.RotateLogsCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.SetClusterNameCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def switches(), do: []
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetDiskFreeLimitCommand do
|
|||
import RabbitMQ.CLI.Ctl.Helpers, only: [memory_unit_absolute: 2]
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
def switches(), do: []
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.SetParameterCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def switches(), do: []
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.SetPermissionsCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.SetPolicyCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def switches(), do: [priority: :integer, apply_to: :string]
|
||||
def aliases(), do: []
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.SetUserTagsCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetVmMemoryHighWatermarkCommand do
|
|||
alias RabbitMQ.CLI.Ctl.Helpers, as: Helpers
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
def switches(), do: []
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.StartAppCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.StopAppCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.StopCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags []
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.SyncQueueCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def merge_defaults([_|_] = args, opts) do
|
||||
{args, Map.merge(default_opts, opts)}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.TraceOffCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def validate([_|_], _), do: {:validation_failure, :too_many_args}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.TraceOnCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
@flags [:vhost]
|
||||
|
||||
def validate([_|_], _), do: {:validation_failure, :too_many_args}
|
||||
|
|
|
|||
|
|
@ -32,17 +32,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.UpdateClusterNodesCommand do
|
|||
def validate(_, _), do: {:validation_failure, :too_many_args}
|
||||
|
||||
def run([seed_node], %{node: node_name}) do
|
||||
ret = :rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_mnesia,
|
||||
:update_cluster_nodes,
|
||||
[Helpers.parse_node(seed_node)]
|
||||
)
|
||||
case ret do
|
||||
{:error, reason} ->
|
||||
{:join_cluster_failed, {reason, node_name}}
|
||||
result ->
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def usage() do
|
||||
|
|
@ -52,4 +46,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.UpdateClusterNodesCommand do
|
|||
def banner([seed_node], %{node: node_name}) do
|
||||
"Will seed #{node_name} from #{seed_node} on next start"
|
||||
end
|
||||
|
||||
def output({:error, :mnesia_unexpectedly_running}, %{node: node_name}) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_software,
|
||||
RabbitMQ.CLI.DefaultOutput.mnesia_running_error(node_name)}
|
||||
end
|
||||
def output({:error, :cannot_cluster_node_with_itself}, %{node: node_name}) do
|
||||
{:error, RabbitMQ.CLI.ExitCodes.exit_software,
|
||||
"Error: cannot cluster node with itself: #{node_name}"}
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,12 +37,46 @@ defmodule RabbitMQ.CLI.Ctl.Commands.WaitCommand do
|
|||
|
||||
def banner(_, %{node: node_name}), do: "Waiting for node #{node_name} ..."
|
||||
|
||||
def output({:error, err}, opts) do
|
||||
case format_error(err) do
|
||||
:undefined -> RabbitMQ.CLI.DefaultOutput.output({:error, err}, opts);
|
||||
error_str -> {:error, RabbitMQ.CLI.ExitCodes.exit_software, error_str}
|
||||
end
|
||||
end
|
||||
def output({:stream, stream}, opts) do
|
||||
Stream.map(stream,
|
||||
fn({:error, err}) ->
|
||||
{:error,
|
||||
case format_error(err) do
|
||||
:undefined -> err;
|
||||
error_str -> error_str
|
||||
end}
|
||||
end)
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
defp format_error(:process_not_running) do
|
||||
"Error: process is not running."
|
||||
end
|
||||
defp format_error({:garbage_in_pid_file, _}) do
|
||||
"Error: garbage in pid file."
|
||||
end
|
||||
defp format_error({:could_not_read_pid, err}) do
|
||||
"Error: could not read pid. Detail: #{err}"
|
||||
end
|
||||
defp format_error(_) do
|
||||
:undefined
|
||||
end
|
||||
|
||||
defp wait_for_application(node, pid_file, :rabbit_and_plugins) do
|
||||
case read_pid_file(pid_file, true) do
|
||||
{:error, _} = err -> err
|
||||
pid ->
|
||||
IO.puts "pid is #{pid}"
|
||||
wait_for_startup(node, pid)
|
||||
{:stream, Stream.concat([["pid is #{pid}"],
|
||||
RabbitMQ.CLI.Ctl.Helpers.defer(
|
||||
fn() ->
|
||||
wait_for_startup(node, pid)
|
||||
end)])}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ defmodule RabbitMQ.CLI.Ctl.Helpers do
|
|||
|
||||
def power_as_int(num, x, y), do: round(num * (:math.pow(x, y)))
|
||||
|
||||
def global_flags, do: [:node, :quiet, :timeout, :longnames]
|
||||
def global_flags, do: [:node, :quiet, :timeout, :longnames, :formatter, :printer, :file]
|
||||
|
||||
def nodes_in_cluster(node, timeout \\ :infinity) do
|
||||
case :rpc.call(node, :rabbit_mnesia, :cluster_nodes, [:running], timeout) do
|
||||
|
|
@ -126,4 +126,12 @@ defmodule RabbitMQ.CLI.Ctl.Helpers do
|
|||
def node_running?(node) do
|
||||
:net_adm.ping(node) == :pong
|
||||
end
|
||||
|
||||
# Convert function to stream
|
||||
def defer(fun) do
|
||||
Stream.iterate(:ok, fn(_) -> fun.() end)
|
||||
|> Stream.drop(1)
|
||||
|> Stream.take(1)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,7 +34,10 @@ defmodule RabbitMQ.CLI.Ctl.Parser do
|
|||
quiet: :boolean,
|
||||
timeout: :integer,
|
||||
vhost: :string,
|
||||
longnames: :boolean]
|
||||
longnames: :boolean,
|
||||
formatter: :string,
|
||||
printer: :string,
|
||||
file: :string]
|
||||
end
|
||||
|
||||
defp build_switches(default) do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
|
||||
defmodule RabbitMQ.CLI.DefaultOutput do
|
||||
alias RabbitMQ.CLI.ExitCodes, as: ExitCodes
|
||||
|
||||
# When `use RabbitMQ.CLI.DefaultOutput` is invoked,
|
||||
# this will define output/2 that delegates to RabbitMQ.CLI.DefaultOutput.output/2.
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
def output(result, opts) do
|
||||
RabbitMQ.CLI.DefaultOutput.output(result, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def output(result, opts) do
|
||||
result
|
||||
|> normalize_output()
|
||||
|> format_output(opts)
|
||||
end
|
||||
|
||||
def mnesia_running_error(node_name) do
|
||||
"Mnesia is still running on node #{node_name}.\n" <>
|
||||
"Please stop RabbitMQ with rabbitmqctl stop_app first."
|
||||
end
|
||||
|
||||
defp normalize_output(:ok), do: :ok
|
||||
defp normalize_output({:ok, _} = input), do: input
|
||||
defp normalize_output({:badrpc, :nodedown} = input), do: input
|
||||
defp normalize_output({:badrpc, :timeout} = input), do: input
|
||||
defp normalize_output({:error, _} = input), do: input
|
||||
defp normalize_output({:error_string, _} = input), do: input
|
||||
defp normalize_output(unknown) when is_atom(unknown), do: {:error, unknown}
|
||||
defp normalize_output({unknown, _} = input) when is_atom(unknown), do: {:error, input}
|
||||
defp normalize_output(result) when not is_atom(result), do: {:ok, result}
|
||||
|
||||
defp format_output({:badrpc, :nodedown} = result, opts) do
|
||||
{:error, ExitCodes.exit_code_for(result),
|
||||
"Error: unable to connect to node '#{opts[:node]}': nodedown"}
|
||||
end
|
||||
defp format_output({:badrpc, :timeout} = result, opts) do
|
||||
{:error, ExitCodes.exit_code_for(result),
|
||||
"Error: {timeout, #{opts[:timeout]}}"}
|
||||
end
|
||||
defp format_output({:error, err} = result, _) do
|
||||
string_err = string_or_inspect(err)
|
||||
{:error, ExitCodes.exit_code_for(result), "Error:\n#{string_err}"}
|
||||
end
|
||||
defp format_output({:error_string, error_string}, _) do
|
||||
{:error, ExitCodes.exit_software, error_string}
|
||||
end
|
||||
defp format_output(:ok, _) do
|
||||
:ok
|
||||
end
|
||||
defp format_output({:ok, output}, _) do
|
||||
case Enumerable.impl_for(output) do
|
||||
nil -> {:ok, output};
|
||||
_ -> {:stream, output}
|
||||
end
|
||||
end
|
||||
|
||||
defp string_or_inspect(val) do
|
||||
case String.Chars.impl_for(val) do
|
||||
nil -> inspect(val);
|
||||
_ -> to_string(val)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -21,10 +21,25 @@ defmodule RabbitMQ.CLI.ExitCodes do
|
|||
@exit_software 70
|
||||
@exit_tempfail 75
|
||||
|
||||
@type exit_code :: integer
|
||||
|
||||
def exit_ok, do: @exit_ok
|
||||
def exit_usage, do: @exit_usage
|
||||
def exit_dataerr, do: @exit_dataerr
|
||||
def exit_unavailable, do: @exit_unavailable
|
||||
def exit_software, do: @exit_software
|
||||
def exit_tempfail, do: @exit_tempfail
|
||||
|
||||
def exit_code_for({:validation_failure, :not_enough_args}), do: exit_usage
|
||||
def exit_code_for({:validation_failure, :too_many_args}), do: exit_usage
|
||||
def exit_code_for({:validation_failure, {:not_enough_args, _}}), do: exit_usage
|
||||
def exit_code_for({:validation_failure, {:too_many_args, _}}), do: exit_usage
|
||||
def exit_code_for({:validation_failure, {:bad_argument, _}}), do: exit_dataerr
|
||||
def exit_code_for({:validation_failure, :bad_argument}), do: exit_dataerr
|
||||
def exit_code_for({:validation_failure, {:bad_option, _}}), do: exit_usage
|
||||
def exit_code_for({:validation_failure, _}), do: exit_usage
|
||||
def exit_code_for({:badrpc, :timeout}), do: exit_tempfail
|
||||
def exit_code_for({:badrpc, :nodedown}), do: exit_unavailable
|
||||
def exit_code_for({:error, _}), do: exit_software
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
# Formats returned values e.g. to human-readable text or JSON.
|
||||
defmodule RabbitMQ.CLI.Formatters.FormatterBehaviour do
|
||||
@callback format_error(String.t, Map.t) :: String.t
|
||||
@callback format_output(any, Map.t) :: String.t
|
||||
@callback format_stream(Enumerable.t, Map.t) :: Enumerable.t
|
||||
end
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
defmodule RabbitMQ.CLI.Formatters.Inspect do
|
||||
@behaviour RabbitMQ.CLI.Formatters.FormatterBehaviour
|
||||
|
||||
def format_error(err, _) when is_binary(err) do
|
||||
err
|
||||
end
|
||||
|
||||
def format_output(output, _) do
|
||||
res = case is_binary(output) do
|
||||
true -> output;
|
||||
false -> inspect(output)
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
def format_stream(stream, options) do
|
||||
elements = Stream.scan(stream, :empty,
|
||||
fn(element, previous) ->
|
||||
separator = case previous do
|
||||
:empty -> "";
|
||||
_ -> ","
|
||||
end
|
||||
format_element(element, separator, options)
|
||||
end)
|
||||
Stream.concat([["["], elements, ["]"]])
|
||||
end
|
||||
|
||||
def format_element({:error, err}, separator, options) do
|
||||
{:error, separator <> format_error(err, options)}
|
||||
end
|
||||
def format_element(val, separator, options) do
|
||||
separator <> format_output(val, options)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
# Basic JSON formatter. Supports 1-level of
|
||||
# collection using start/finish_collection.
|
||||
# Primary purpose is to translate stream from CTL,
|
||||
# so there is no need for multiple collection levels
|
||||
|
||||
defmodule RabbitMQ.CLI.Formatters.Json do
|
||||
@behaviour RabbitMQ.CLI.Formatters.FormatterBehaviour
|
||||
|
||||
def format_error(err, _) when is_binary(err) do
|
||||
{:ok, json} = JSON.encode(%{error: err})
|
||||
json
|
||||
end
|
||||
|
||||
def format_output(output, _) do
|
||||
{:ok, json} = JSON.encode(output)
|
||||
json
|
||||
end
|
||||
|
||||
def format_stream(stream, options) do
|
||||
elements = Stream.scan(stream, :empty,
|
||||
fn(element, previous) ->
|
||||
separator = case previous do
|
||||
:empty -> "";
|
||||
_ -> ","
|
||||
end
|
||||
format_element(element, separator, options)
|
||||
end)
|
||||
Stream.concat([["["], elements, ["]"]])
|
||||
end
|
||||
|
||||
def format_element({:error, err}, separator, options) do
|
||||
{:error, separator <> format_error(err, options)}
|
||||
end
|
||||
def format_element(val, separator, options) do
|
||||
separator <> format_output(val, options)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
|
||||
defmodule RabbitMQ.CLI.Output do
|
||||
|
||||
alias RabbitMQ.CLI.ExitCodes, as: ExitCodes
|
||||
|
||||
def format_output(output, options) do
|
||||
formatter = get_formatter(options)
|
||||
result = format_output_0(output, formatter, options)
|
||||
result
|
||||
end
|
||||
|
||||
def format_output_0({:error, exit_code, error_string}, formatter, options) do
|
||||
{:error, exit_code, formatter.format_error(error_string, options)}
|
||||
end
|
||||
def format_output_0(:ok, _, _) do
|
||||
:ok
|
||||
end
|
||||
def format_output_0({:ok, output}, formatter, options) do
|
||||
{:ok, formatter.format_output(output, options)}
|
||||
end
|
||||
def format_output_0({:stream, stream}, formatter, options) do
|
||||
{:stream, formatter.format_stream(stream, options)}
|
||||
end
|
||||
|
||||
def print_output(output, options) do
|
||||
printer = get_printer(options)
|
||||
{:ok, printer_state} = printer.init(options)
|
||||
result = print_output_0(output, printer, printer_state)
|
||||
printer.finish(printer_state)
|
||||
result
|
||||
end
|
||||
|
||||
def print_output_0({:error, exit_code, string}, printer, printer_state) do
|
||||
printer.print_error(string, printer_state)
|
||||
exit_code
|
||||
end
|
||||
def print_output_0(:ok, printer, printer_state) do
|
||||
printer.print_ok(printer_state)
|
||||
ExitCodes.exit_ok
|
||||
end
|
||||
def print_output_0({:ok, single_value}, printer, printer_state) do
|
||||
printer.print_output(single_value, printer_state)
|
||||
ExitCodes.exit_ok
|
||||
end
|
||||
def print_output_0({:stream, stream}, printer, printer_state) do
|
||||
case print_output_stream(stream, printer, printer_state) do
|
||||
:ok -> ExitCodes.exit_ok;
|
||||
{:error, _} = err -> ExitCodes.exit_code_for(err)
|
||||
end
|
||||
end
|
||||
|
||||
def print_output_stream(stream, printer, printer_state) do
|
||||
Enum.reduce_while(stream, :ok,
|
||||
fn
|
||||
({:error, err}, _) ->
|
||||
printer.print_error(err, printer_state)
|
||||
{:halt, {:error, err}};
|
||||
(val, _) ->
|
||||
printer.print_output(val, printer_state)
|
||||
{:cont, :ok}
|
||||
end)
|
||||
end
|
||||
|
||||
def get_printer(%{printer: printer}) do
|
||||
module_name = String.to_atom("RabbitMQ.CLI.Printers." <>
|
||||
Mix.Utils.camelize(printer))
|
||||
case Code.ensure_loaded(module_name) do
|
||||
{:module, _} -> module_name;
|
||||
{:error, :nofile} -> default_printer
|
||||
end
|
||||
end
|
||||
def get_printer(_) do
|
||||
default_printer
|
||||
end
|
||||
|
||||
def get_formatter(%{formatter: formatter}) do
|
||||
module_name = Module.safe_concat("RabbitMQ.CLI.Formatters", Mix.Utils.camelize(formatter))
|
||||
case Code.ensure_loaded(module_name) do
|
||||
{:module, _} -> module_name;
|
||||
{:error, :nofile} -> default_formatter
|
||||
end
|
||||
end
|
||||
def get_formatter(_) do
|
||||
default_formatter
|
||||
end
|
||||
|
||||
def default_printer() do
|
||||
RabbitMQ.CLI.Printers.StdIO
|
||||
end
|
||||
|
||||
def default_formatter() do
|
||||
RabbitMQ.CLI.Formatters.Inspect
|
||||
end
|
||||
end
|
||||
|
|
@ -20,6 +20,7 @@ defmodule RabbitMQ.CLI.Plugins.Commands.DisableCommand do
|
|||
alias RabbitMQ.CLI.Ctl.Helpers, as: Helpers
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{online: true, offline: false}, opts)}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ defmodule RabbitMQ.CLI.Plugins.Commands.EnableCommand do
|
|||
alias RabbitMQ.CLI.Ctl.Helpers, as: Helpers
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{online: true, offline: false}, opts)}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ defmodule RabbitMQ.CLI.Plugins.Commands.ListCommand do
|
|||
alias RabbitMQ.CLI.Plugins.Helpers, as: PluginHelpers
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def merge_defaults([], opts), do: merge_defaults([".*"], opts)
|
||||
def merge_defaults(args, opts), do: {args, Map.merge(default_opts, opts)}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ defmodule RabbitMQ.CLI.Plugins.Commands.SetCommand do
|
|||
alias RabbitMQ.CLI.Ctl.Helpers, as: Helpers
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def merge_defaults(args, %{offline: false, online: false} = opts) do
|
||||
{args, Map.merge(%{online: true, offline: false}, opts)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
defmodule RabbitMQ.CLI.Printers.File do
|
||||
|
||||
def init(options) do
|
||||
file = options[:file]
|
||||
case File.open(file) do
|
||||
{:ok, io_device} -> {:ok, %{device: io_device}};
|
||||
{:error, err} -> {:error, err}
|
||||
end
|
||||
end
|
||||
def finish(%{device: io_device}) do
|
||||
:ok = File.close(io_device)
|
||||
end
|
||||
|
||||
def print_error(err, %{device: io_device}) do
|
||||
IO.puts(io_device, err)
|
||||
end
|
||||
|
||||
def print_output(output, %{device: io_device}) do
|
||||
IO.puts(io_device, output)
|
||||
end
|
||||
|
||||
def print_ok(_) do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
defmodule RabbitMQ.CLI.Printers.StdIO do
|
||||
|
||||
def init(_), do: {:ok, :ok}
|
||||
def finish(_), do: :ok
|
||||
|
||||
def print_error(nil, _), do: :ok
|
||||
def print_error(err, _) do
|
||||
IO.puts(err)
|
||||
end
|
||||
|
||||
def print_output(nil, _), do: :ok
|
||||
def print_output(output, _) do
|
||||
IO.puts(output)
|
||||
end
|
||||
|
||||
def print_ok(_) do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
defmodule RabbitMQ.CLI.StandardCodes do
|
||||
|
||||
def map_to_standard_code(:ok), do: :ok
|
||||
def map_to_standard_code({:ok, _} = input), do: input
|
||||
def map_to_standard_code({:badrpc, :nodedown} = input), do: input
|
||||
def map_to_standard_code({:badrpc, :timeout} = input), do: input
|
||||
def map_to_standard_code({:refused, _, _, _} = input), do: input
|
||||
def map_to_standard_code({:bad_option, _} = input), do: input
|
||||
def map_to_standard_code({:error, _} = input), do: input
|
||||
def map_to_standard_code({:join_cluster_failed, _} = input), do: input
|
||||
def map_to_standard_code({:reset_failed, _} = input), do: input
|
||||
def map_to_standard_code({:validation_failure, _} = input), do: input
|
||||
def map_to_standard_code({:healthcheck_failed, _} = input), do: input
|
||||
def map_to_standard_code(unknown) when is_atom(unknown), do: {:error, unknown}
|
||||
def map_to_standard_code({unknown, _} = input) when is_atom(unknown), do: {:error, input}
|
||||
def map_to_standard_code(result) when not is_atom(result), do: {:ok, result}
|
||||
end
|
||||
|
|
@ -16,14 +16,15 @@
|
|||
|
||||
defmodule RabbitMQCtl do
|
||||
alias RabbitMQ.CLI.Distribution, as: Distribution
|
||||
alias RabbitMQ.CLI.StandardCodes, as: StandardCodes
|
||||
|
||||
alias RabbitMQ.CLI.Ctl.Commands.HelpCommand, as: HelpCommand
|
||||
alias RabbitMQ.CLI.Output, as: Output
|
||||
|
||||
import RabbitMQ.CLI.Ctl.Helpers
|
||||
import RabbitMQ.CLI.Ctl.Parser
|
||||
import RabbitMQ.CLI.ExitCodes
|
||||
|
||||
|
||||
def main(["--auto-complete", "./rabbitmqctl " <> str]) do
|
||||
auto_complete(str)
|
||||
end
|
||||
|
|
@ -32,24 +33,32 @@ defmodule RabbitMQCtl do
|
|||
end
|
||||
def main(unparsed_command) do
|
||||
{parsed_cmd, options, invalid} = parse(unparsed_command)
|
||||
case try_run_command(parsed_cmd, options, invalid) do
|
||||
{:validation_failure, _} = invalid ->
|
||||
error = validation_error(invalid, unparsed_command)
|
||||
{:error, exit_code_for(invalid), error}
|
||||
cmd_result -> cmd_result
|
||||
end
|
||||
|> Output.format_output(options)
|
||||
|> Output.print_output(options)
|
||||
|> exit_program
|
||||
end
|
||||
|
||||
def try_run_command(parsed_cmd, options, invalid) do
|
||||
case {is_command?(parsed_cmd), invalid} do
|
||||
## No such command
|
||||
{false, _} ->
|
||||
HelpCommand.all_usage() |> handle_exit(exit_usage);
|
||||
usage_string = HelpCommand.all_usage()
|
||||
{:error, exit_usage, usage_string};
|
||||
## Invalid options
|
||||
{_, [_|_]} ->
|
||||
print_standard_messages({:bad_option, invalid}, unparsed_command)
|
||||
|> handle_exit
|
||||
{:validation_failure, {:bad_option, invalid}};
|
||||
## Command valid
|
||||
{true, []} ->
|
||||
effective_options = options |> merge_all_defaults |> normalize_node
|
||||
Distribution.start(effective_options)
|
||||
|
||||
effective_options
|
||||
|> run_command(parsed_cmd)
|
||||
|> StandardCodes.map_to_standard_code
|
||||
|> print_standard_messages(unparsed_command)
|
||||
|> handle_exit
|
||||
run_command(effective_options, parsed_cmd)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -81,7 +90,7 @@ defmodule RabbitMQCtl do
|
|||
connect_to_rabbitmq(node)
|
||||
end
|
||||
|
||||
defp run_command(_, []), do: HelpCommand.all_usage()
|
||||
defp run_command(_, []), do: {:error, exit_ok, HelpCommand.all_usage()}
|
||||
defp run_command(options, [command_name | arguments]) do
|
||||
with_command(command_name,
|
||||
fn(command) ->
|
||||
|
|
@ -92,10 +101,12 @@ defmodule RabbitMQCtl do
|
|||
:ok ->
|
||||
print_banner(command, arguments, options)
|
||||
maybe_connect_to_rabbitmq(command_name, options[:node])
|
||||
execute_command(command, arguments, options)
|
||||
|
||||
command.run(arguments, options)
|
||||
|> command.output(options)
|
||||
err -> err
|
||||
end
|
||||
result -> {:bad_option, result}
|
||||
result -> {:validation_failure, {:bad_option, result}}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
@ -112,163 +123,40 @@ defmodule RabbitMQCtl do
|
|||
end
|
||||
end
|
||||
|
||||
defp execute_command(command, arguments, options) do
|
||||
command.run(arguments, options)
|
||||
end
|
||||
|
||||
defp print_standard_messages({:badrpc, :nodedown} = result, unparsed_command) do
|
||||
{_, options, _} = parse(unparsed_command)
|
||||
|
||||
IO.puts "Error: unable to connect to node '#{options[:node]}': nodedown"
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:badrpc, :timeout} = result, unparsed_command) do
|
||||
{_, options, _} = parse(unparsed_command)
|
||||
|
||||
IO.puts "Error: {timeout, #{options[:timeout]}}"
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:too_many_args, _} = result, unparsed_command) do
|
||||
{[cmd | _], _, _} = parse(unparsed_command)
|
||||
|
||||
IO.puts "Error: too many arguments."
|
||||
IO.puts "Given:\n\t#{unparsed_command |> Enum.join(" ")}"
|
||||
HelpCommand.run([cmd], %{})
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:not_enough_args, _} = result, unparsed_command) do
|
||||
{[cmd | _], _, _} = parse(unparsed_command)
|
||||
|
||||
IO.puts "Error: not enough arguments."
|
||||
IO.puts "Given:\n\t#{unparsed_command |> Enum.join(" ")}"
|
||||
HelpCommand.run([cmd], %{})
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:refused, user, _, _} = result, _) do
|
||||
IO.puts "Error: failed to authenticate user \"#{user}\""
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages(
|
||||
{failed_command,
|
||||
{:mnesia_unexpectedly_running, node_name}} = result, _)
|
||||
when
|
||||
failed_command == :reset_failed or
|
||||
failed_command == :join_cluster_failed or
|
||||
failed_command == :rename_node_failed or
|
||||
failed_command == :change_node_type_failed
|
||||
do
|
||||
IO.puts "Mnesia is still running on node #{node_name}."
|
||||
IO.puts "Please stop RabbitMQ with rabbitmqctl stop_app first."
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:error, :process_not_running} = result, _) do
|
||||
IO.puts "Error: process is not running."
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:error, {:garbage_in_pid_file, _}} = result, _) do
|
||||
IO.puts "Error: garbage in pid file."
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:error, {:could_not_read_pid, err}} = result, _) do
|
||||
IO.puts "Error: could not read pid. Detail: #{err}"
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:error, err} = result, _) do
|
||||
IO.puts "Error:"
|
||||
IO.inspect err
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:healthcheck_failed, message} = result, _) do
|
||||
IO.puts "Error: healthcheck failed. Message: #{message}"
|
||||
result
|
||||
end
|
||||
|
||||
defp print_standard_messages({:bad_option, opt} = result, unparsed_command) do
|
||||
case parse(unparsed_command) do
|
||||
{[cmd | _], _, _} ->
|
||||
IO.puts "Error: invalid options for this command."
|
||||
IO.puts "Given:\n\t#{unparsed_command |> Enum.join(" ")}"
|
||||
HelpCommand.run([cmd], %{})
|
||||
result;
|
||||
_ ->
|
||||
IO.puts "Error: invalid options"
|
||||
IO.inspect opt
|
||||
HelpCommand.all_usage()
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp print_standard_messages({:validation_failure, err_detail} = result, unparsed_command) do
|
||||
defp validation_error({:validation_failure, err_detail}, unparsed_command) do
|
||||
{[command_name | _], _, _} = parse(unparsed_command)
|
||||
err = format_validation_error(err_detail) # TODO format the error better
|
||||
IO.puts "Error: #{err}"
|
||||
IO.puts "Given:\n\t#{unparsed_command |> Enum.join(" ")}"
|
||||
err = format_validation_error(err_detail, command_name) # TODO format the error better
|
||||
base_error = "Error: #{err}\nGiven:\n\t#{unparsed_command |> Enum.join(" ")}"
|
||||
|
||||
case is_command?(command_name) do
|
||||
usage = case is_command?(command_name) do
|
||||
true ->
|
||||
command = commands[command_name]
|
||||
HelpCommand.print_base_usage(HelpCommand.program_name(), command)
|
||||
HelpCommand.base_usage(HelpCommand.program_name(), command)
|
||||
false ->
|
||||
HelpCommand.all_usage()
|
||||
exit_usage
|
||||
end
|
||||
|
||||
result
|
||||
base_error <> "\n" <> usage
|
||||
end
|
||||
|
||||
defp print_standard_messages(result, _) do
|
||||
result
|
||||
end
|
||||
|
||||
defp format_validation_error(:not_enough_args), do: "not enough arguments."
|
||||
defp format_validation_error({:not_enough_args, detail}), do: "not enough arguments. #{detail}"
|
||||
defp format_validation_error(:too_many_args), do: "too many arguments."
|
||||
defp format_validation_error({:too_many_args, detail}), do: "too many arguments. #{detail}"
|
||||
defp format_validation_error(:bad_argument), do: "Bad argument."
|
||||
defp format_validation_error({:bad_argument, detail}), do: "Bad argument. #{detail}"
|
||||
defp format_validation_error(err), do: inspect err
|
||||
|
||||
defp handle_exit({:validation_failure, :not_enough_args}), do: exit_program(exit_usage)
|
||||
defp handle_exit({:validation_failure, :too_many_args}), do: exit_program(exit_usage)
|
||||
defp handle_exit({:validation_failure, {:not_enough_args, _}}), do: exit_program(exit_usage)
|
||||
defp handle_exit({:validation_failure, {:too_many_args, _}}), do: exit_program(exit_usage)
|
||||
defp handle_exit({:validation_failure, {:bad_argument, _}}), do: exit_program(exit_dataerr)
|
||||
defp handle_exit({:validation_failure, :bad_argument}), do: exit_program(exit_dataerr)
|
||||
defp handle_exit({:validation_failure, _}), do: exit_program(exit_usage)
|
||||
defp handle_exit({:bad_option, _} = _err), do: exit_program(exit_usage)
|
||||
defp handle_exit({:badrpc, :timeout}), do: exit_program(exit_tempfail)
|
||||
defp handle_exit({:badrpc, :nodedown}), do: exit_program(exit_unavailable)
|
||||
defp handle_exit({:refused, _, _, _}), do: exit_program(exit_dataerr)
|
||||
defp handle_exit({:healthcheck_failed, _}), do: exit_program(exit_software)
|
||||
defp handle_exit({:join_cluster_failed, _}), do: exit_program(exit_software)
|
||||
defp handle_exit({:reset_failed, _}), do: exit_program(exit_software)
|
||||
defp handle_exit({:error, _}), do: exit_program(exit_software)
|
||||
defp handle_exit(true), do: handle_exit(:ok, exit_ok)
|
||||
defp handle_exit(:ok), do: handle_exit(:ok, exit_ok)
|
||||
defp handle_exit({:ok, result}), do: handle_exit({:ok, result}, exit_ok)
|
||||
defp handle_exit(result) when is_list(result), do: handle_exit({:ok, result}, exit_ok)
|
||||
defp handle_exit(:ok, code), do: exit_program(code)
|
||||
defp handle_exit({:ok, result}, code) do
|
||||
case Enumerable.impl_for(result) do
|
||||
nil -> IO.inspect result;
|
||||
_ -> result |> Stream.map(&IO.inspect/1) |> Stream.run
|
||||
defp format_validation_error(:not_enough_args, _), do: "not enough arguments."
|
||||
defp format_validation_error({:not_enough_args, detail}, _), do: "not enough arguments. #{detail}"
|
||||
defp format_validation_error(:too_many_args, _), do: "too many arguments."
|
||||
defp format_validation_error({:too_many_args, detail}, _), do: "too many arguments. #{detail}"
|
||||
defp format_validation_error(:bad_argument, _), do: "Bad argument."
|
||||
defp format_validation_error({:bad_argument, detail}, _), do: "Bad argument. #{detail}"
|
||||
defp format_validation_error({:bad_option, opts}, command_name) do
|
||||
header = case is_command?(command_name) do
|
||||
true -> "Invalid options for this command:";
|
||||
false -> "Invalid options:"
|
||||
end
|
||||
exit_program(code)
|
||||
Enum.join([header | for {key, val} <- opts do "#{key} : #{val}" end], "\n")
|
||||
end
|
||||
defp format_validation_error(err, _), do: inspect err
|
||||
|
||||
defp invalid_flags(command, opts) do
|
||||
Map.keys(opts) -- (command.flags ++ global_flags)
|
||||
Map.take(opts, Map.keys(opts) -- (command.flags ++ global_flags))
|
||||
|> Map.to_list
|
||||
end
|
||||
|
||||
defp exit_program(code) do
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ defmodule RabbitMQCtl.MixfileBase do
|
|||
|> add_modules(Mix.env)
|
||||
end
|
||||
|
||||
|
||||
|
||||
defp add_modules(app, :test) do
|
||||
path = Mix.Project.compile_path
|
||||
mods = modules_from(Path.wildcard("#{path}/*.beam"))
|
||||
|
|
@ -103,6 +103,9 @@ defmodule RabbitMQCtl.MixfileBase do
|
|||
:amqp,
|
||||
git: "https://github.com/pma/amqp.git",
|
||||
branch: "master"
|
||||
},
|
||||
{
|
||||
:json, "~> 0.3.0"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ defmodule AddVhostCommandTest do
|
|||
test "run: Adding the same host twice results in a host exists message", context do
|
||||
add_vhost context[:vhost]
|
||||
|
||||
assert @command.run([context[:vhost]], context[:opts]) ==
|
||||
assert @command.run([context[:vhost]], context[:opts]) ==
|
||||
{:error, {:vhost_already_exists, context[:vhost]}}
|
||||
|
||||
assert list_vhosts |> Enum.count(fn(record) -> record[:name] == context[:vhost] end) == 1
|
||||
|
|
|
|||
|
|
@ -77,6 +77,15 @@ defmodule AuthenticateUserCommandTest do
|
|||
assert @command.banner([context[:user], context[:password]], context[:opts])
|
||||
=~ ~r/Authenticating user/
|
||||
assert @command.banner([context[:user], context[:password]], context[:opts])
|
||||
=~ ~r/"#{context[:user]}"/
|
||||
=~ ~r/"#{context[:user]}"/
|
||||
end
|
||||
|
||||
test "output: refused error", context do
|
||||
user = "example_user"
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_dataerr
|
||||
assert match?({:error, ^exit_code,
|
||||
"Error: failed to authenticate user \"example_user\"\n" <>
|
||||
"Unable to foo"},
|
||||
@command.output({:refused, user, "Unable to ~s", ["foo"]}, context[:opts]))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ defmodule ChangeClusterNodeTypeCommandTest do
|
|||
|
||||
test "run: request to a node with running RabbitMQ app fails", context do
|
||||
assert match?(
|
||||
{:change_node_type_failed, {:mnesia_unexpectedly_running, _}},
|
||||
{:error, :mnesia_unexpectedly_running},
|
||||
@command.run(["ram"], context[:opts]))
|
||||
end
|
||||
|
||||
|
|
@ -90,4 +90,12 @@ defmodule ChangeClusterNodeTypeCommandTest do
|
|||
assert @command.banner(["ram"], context[:opts]) =~
|
||||
~r/Turning #{get_rabbit_hostname} into a ram node/
|
||||
end
|
||||
|
||||
test "output mnesia is running error", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code,
|
||||
"Mnesia is still running on node " <> _},
|
||||
@command.output({:error, :mnesia_unexpectedly_running}, context[:opts]))
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ defmodule ClearParameterCommandTest do
|
|||
end
|
||||
|
||||
test "validate: argument validation" do
|
||||
assert @command.validate(["one", "two"], %{}) == :ok
|
||||
assert @command.validate(["one", "two"], %{}) == :ok
|
||||
assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
|
||||
assert @command.validate(["insufficient"], %{}) == {:validation_failure, :not_enough_args}
|
||||
assert @command.validate(["this", "is", "many"], %{}) == {:validation_failure, :too_many_args}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ defmodule ClusterStatusCommandTest do
|
|||
|
||||
test "banner", context do
|
||||
s = @command.banner([], context[:opts])
|
||||
|
||||
|
||||
assert s =~ ~r/Cluster status of node/
|
||||
assert s =~ ~r/#{get_rabbit_hostname}/
|
||||
end
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ end
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.DuckCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
def usage(), do: ["duck"]
|
||||
def flags(), do: []
|
||||
def validate(_,_), do: :ok
|
||||
|
|
@ -133,6 +134,7 @@ end
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.GrayGooseCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
def usage(), do: ["gray_goose"]
|
||||
def flags(), do: []
|
||||
def validate(_,_), do: :ok
|
||||
|
|
@ -151,6 +153,7 @@ end
|
|||
|
||||
defmodule RabbitMQ.CLI.Plugins.Commands.StorkCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
def usage(), do: ["stork"]
|
||||
def flags(), do: []
|
||||
def validate(_,_), do: :ok
|
||||
|
|
@ -163,6 +166,7 @@ end
|
|||
|
||||
defmodule RabbitMQ.CLI.Plugins.Commands.HeronCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
def usage(), do: ["heron"]
|
||||
def flags(), do: []
|
||||
def validate(_,_), do: :ok
|
||||
|
|
@ -177,6 +181,7 @@ end
|
|||
|
||||
defmodule RabbitMQ.CLI.Custom.Commands.CrowCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
def usage(), do: ["crow"]
|
||||
def flags(), do: []
|
||||
def validate(_,_), do: :ok
|
||||
|
|
@ -190,6 +195,7 @@ end
|
|||
|
||||
defmodule RabbitMQ.CLI.Custom.Commands.RavenCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
def usage(), do: ["raven"]
|
||||
def flags(), do: []
|
||||
def validate(_,_), do: :ok
|
||||
|
|
@ -202,6 +208,7 @@ end
|
|||
|
||||
defmodule RabbitMQ.CLI.Seagull.Commands.SeagullCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
def usage(), do: ["seagull"]
|
||||
def flags(), do: []
|
||||
def validate(_,_), do: :ok
|
||||
|
|
|
|||
|
|
@ -0,0 +1,157 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
|
||||
defmodule DefaultOutputTest do
|
||||
use ExUnit.Case, async: false
|
||||
import RabbitMQ.CLI.ExitCodes
|
||||
|
||||
test "ok is passed as is" do
|
||||
assert match?(:ok, ExampleCommand.output(:ok, %{}))
|
||||
end
|
||||
|
||||
test "ok with message is passed as is" do
|
||||
assert match?({:ok, :message}, ExampleCommand.output({:ok, :message}, %{}))
|
||||
assert match?({:ok, {:complex, "message"}}, ExampleCommand.output({:ok, {:complex, "message"}}, %{}))
|
||||
end
|
||||
|
||||
test "enumerable is passed as stream" do
|
||||
assert match?({:stream, 'list'}, ExampleCommand.output({:ok, 'list'}, %{}))
|
||||
assert match?({:stream, 'list'}, ExampleCommand.output('list', %{}))
|
||||
|
||||
assert match?({:stream, [1,2,3]}, ExampleCommand.output({:ok, [1,2,3]}, %{}))
|
||||
assert match?({:stream, [1,2,3]}, ExampleCommand.output([1,2,3], %{}))
|
||||
|
||||
stream = Stream.timer(10000)
|
||||
assert match?({:stream, ^stream}, ExampleCommand.output({:ok, stream}, %{}))
|
||||
assert match?({:stream, ^stream}, ExampleCommand.output(stream, %{}))
|
||||
end
|
||||
|
||||
test "badrpc nodedown error" do
|
||||
exit_code = exit_unavailable
|
||||
node = :example@node
|
||||
assert match?({:error, ^exit_code, "Error: unable to connect to node 'example@node': nodedown"},
|
||||
ExampleCommand.output({:badrpc, :nodedown}, %{node: node}))
|
||||
end
|
||||
|
||||
test "badrpc timeout error" do
|
||||
exit_code = exit_tempfail
|
||||
timeout = 1000
|
||||
assert match?({:error, ^exit_code, "Error: {timeout, 1000}"},
|
||||
ExampleCommand.output({:badrpc, :timeout}, %{timeout: timeout}))
|
||||
end
|
||||
|
||||
test "generic error" do
|
||||
exit_code = exit_software
|
||||
assert match?({:error, ^exit_code, "Error:\nerror message"},
|
||||
ExampleCommand.output({:error, "error message"}, %{}))
|
||||
end
|
||||
|
||||
test "inspect arbitrary error" do
|
||||
exit_code = exit_software
|
||||
error = %{i: [am: "arbitrary", error: 1]}
|
||||
inspected = inspect(error)
|
||||
assert match?({:error, ^exit_code, "Error:\n" <> ^inspected},
|
||||
ExampleCommand.output({:error, error}, %{}))
|
||||
end
|
||||
|
||||
test "atom error" do
|
||||
exit_code = exit_software
|
||||
assert match?({:error, ^exit_code, "Error:\nerror_message"},
|
||||
ExampleCommand.output({:error, :error_message}, %{}))
|
||||
end
|
||||
|
||||
test "unknown atom is error" do
|
||||
exit_code = exit_software
|
||||
assert match?({:error, ^exit_code, "Error:\nerror_message"},
|
||||
ExampleCommand.output(:error_message, %{}))
|
||||
end
|
||||
|
||||
test "unknown tuple is error" do
|
||||
exit_code = exit_software
|
||||
assert match?({:error, ^exit_code, "Error:\n{:left, :right}"},
|
||||
ExampleCommand.output({:left, :right}, %{}))
|
||||
end
|
||||
|
||||
test "error_string is error" do
|
||||
exit_code = exit_software
|
||||
assert match?({:error, ^exit_code, "I am string"},
|
||||
ExampleCommand.output({:error_string, "I am string"}, %{}))
|
||||
end
|
||||
|
||||
test "non atom value is ok" do
|
||||
val = "foo"
|
||||
assert match?({:ok, ^val}, ExampleCommand.output(val, %{}))
|
||||
val = 125
|
||||
assert match?({:ok, ^val}, ExampleCommand.output(val, %{}))
|
||||
val = 100.2
|
||||
assert match?({:ok, ^val}, ExampleCommand.output(val, %{}))
|
||||
val = {:one, :two, :three}
|
||||
assert match?({:ok, ^val}, ExampleCommand.output(val, %{}))
|
||||
end
|
||||
|
||||
test "custom output function can be defined" do
|
||||
assert match?({:error, 125, "Non standard"},
|
||||
ExampleCommandWithCustomOutput.output(:non_standard_output, %{}))
|
||||
end
|
||||
|
||||
test "default output works even if custom output is defined" do
|
||||
assert match?(:ok, ExampleCommandWithCustomOutput.output(:ok, %{}))
|
||||
assert match?({:ok, {:complex, "message"}},
|
||||
ExampleCommandWithCustomOutput.output({:ok, {:complex, "message"}}, %{}))
|
||||
|
||||
assert match?({:stream, [1,2,3]}, ExampleCommandWithCustomOutput.output({:ok, [1,2,3]}, %{}))
|
||||
assert match?({:stream, [1,2,3]}, ExampleCommandWithCustomOutput.output([1,2,3], %{}))
|
||||
|
||||
exit_code = exit_unavailable
|
||||
node = :example@node
|
||||
assert match?({:error, ^exit_code, "Error: unable to connect to node 'example@node': nodedown"},
|
||||
ExampleCommandWithCustomOutput.output({:badrpc, :nodedown}, %{node: node}))
|
||||
|
||||
exit_code = exit_tempfail
|
||||
timeout = 1000
|
||||
assert match?({:error, ^exit_code, "Error: {timeout, 1000}"},
|
||||
ExampleCommandWithCustomOutput.output({:badrpc, :timeout}, %{timeout: timeout}))
|
||||
|
||||
exit_code = exit_software
|
||||
error = %{i: [am: "arbitrary", error: 1]}
|
||||
inspected = inspect(error)
|
||||
assert match?({:error, ^exit_code, "Error:\n" <> ^inspected},
|
||||
ExampleCommandWithCustomOutput.output({:error, error}, %{}))
|
||||
|
||||
assert match?({:error, ^exit_code, "I am string"},
|
||||
ExampleCommandWithCustomOutput.output({:error_string, "I am string"}, %{}))
|
||||
|
||||
val = "foo"
|
||||
assert match?({:ok, ^val}, ExampleCommandWithCustomOutput.output(val, %{}))
|
||||
val = 125
|
||||
assert match?({:ok, ^val}, ExampleCommandWithCustomOutput.output(val, %{}))
|
||||
val = 100.2
|
||||
assert match?({:ok, ^val}, ExampleCommandWithCustomOutput.output(val, %{}))
|
||||
val = {:one, :two, :three}
|
||||
assert match?({:ok, ^val}, ExampleCommandWithCustomOutput.output(val, %{}))
|
||||
end
|
||||
end
|
||||
|
||||
defmodule ExampleCommand do
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
end
|
||||
|
||||
defmodule ExampleCommandWithCustomOutput do
|
||||
def output(:non_standard_output, _) do
|
||||
{:error, 125, "Non standard"}
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
end
|
||||
|
|
@ -41,7 +41,7 @@ defmodule DeleteVhostCommandTest do
|
|||
end
|
||||
|
||||
test "validate: argument count validates" do
|
||||
assert @command.validate(["tst"], %{}) == :ok
|
||||
assert @command.validate(["tst"], %{}) == :ok
|
||||
assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
|
||||
assert @command.validate(["test", "extra"], %{}) == {:validation_failure, :too_many_args}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ defmodule EnvironmentCommandTest do
|
|||
end
|
||||
|
||||
test "validate: argument count validates" do
|
||||
assert @command.validate([], %{}) == :ok
|
||||
assert @command.validate([], %{}) == :ok
|
||||
assert @command.validate(["extra"], %{}) == {:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ defmodule ForceResetCommandTest do
|
|||
test "run: reset request to an active node with a running rabbit app fails", context do
|
||||
add_vhost "some_vhost"
|
||||
assert vhost_exists? "some_vhost"
|
||||
assert match?({:reset_failed, {:mnesia_unexpectedly_running, _}}, @command.run([], context[:opts]))
|
||||
assert match?({:error, :mnesia_unexpectedly_running}, @command.run([], context[:opts]))
|
||||
assert vhost_exists? "some_vhost"
|
||||
end
|
||||
|
||||
|
|
@ -71,4 +71,12 @@ defmodule ForceResetCommandTest do
|
|||
test "banner", context do
|
||||
assert @command.banner([], context[:opts]) =~ ~r/Forcefully resetting node #{get_rabbit_hostname}/
|
||||
end
|
||||
|
||||
test "output mnesia is running error", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code,
|
||||
"Mnesia is still running on node " <> _},
|
||||
@command.output({:error, :mnesia_unexpectedly_running}, context[:opts]))
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
defmodule HelpCommandTest do
|
||||
use ExUnit.Case, async: false
|
||||
import ExUnit.CaptureIO
|
||||
|
||||
alias RabbitMQ.CLI.Ctl.Helpers, as: Helpers
|
||||
|
||||
|
|
@ -27,55 +26,53 @@ defmodule HelpCommandTest do
|
|||
end
|
||||
|
||||
test "basic usage info is printed" do
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/Default node is \"rabbit@server\"/
|
||||
assert @command.run([], %{}) =~ ~r/Default node is \"rabbit@server\"/
|
||||
end
|
||||
|
||||
test "command usage info is printed if command is specified" do
|
||||
Helpers.commands
|
||||
|> Map.keys
|
||||
|> Enum.each(
|
||||
fn(command) -> assert capture_io(
|
||||
fn -> @command.run([command], %{}) end
|
||||
) =~ ~r/#{command}/
|
||||
end)
|
||||
fn(command) ->
|
||||
assert @command.run([command], %{}) =~ ~r/#{command}/
|
||||
end)
|
||||
end
|
||||
|
||||
test "Command info is printed" do
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/Commands:\n/
|
||||
assert @command.run([], %{}) =~ ~r/Commands:\n/
|
||||
|
||||
# Checks to verify that each module's command appears in the list.
|
||||
Helpers.commands
|
||||
|> Map.keys
|
||||
|> Enum.each(
|
||||
fn(command) -> assert capture_io(
|
||||
fn -> @command.run([], %{}) end
|
||||
) =~ ~r/\n #{command}.*\n/
|
||||
end)
|
||||
fn(command) ->
|
||||
assert @command.run([], %{}) =~ ~r/\n #{command}.*\n/
|
||||
end)
|
||||
end
|
||||
|
||||
test "Info items are defined for existing commands" do
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/\n\<vhostinfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/\n\<queueinfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/\n\<exchangeinfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/\n\<bindinginfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/\n\<connectioninfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/\n\<channelinfoitem\> .*\n/
|
||||
assert @command.run([], %{}) =~ ~r/\n\<vhostinfoitem\> .*\n/
|
||||
assert @command.run([], %{}) =~ ~r/\n\<queueinfoitem\> .*\n/
|
||||
assert @command.run([], %{}) =~ ~r/\n\<exchangeinfoitem\> .*\n/
|
||||
assert @command.run([], %{}) =~ ~r/\n\<bindinginfoitem\> .*\n/
|
||||
assert @command.run([], %{}) =~ ~r/\n\<connectioninfoitem\> .*\n/
|
||||
assert @command.run([], %{}) =~ ~r/\n\<channelinfoitem\> .*\n/
|
||||
end
|
||||
|
||||
test "Info items are printed for selected command" do
|
||||
assert capture_io(fn -> @command.run(["list_vhosts"], %{}) end) =~ ~r/\n\<vhostinfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run(["list_queues"], %{}) end) =~ ~r/\n\<queueinfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run(["list_exchanges"], %{}) end) =~ ~r/\n\<exchangeinfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run(["list_bindings"], %{}) end) =~ ~r/\n\<bindinginfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run(["list_connections"], %{}) end) =~ ~r/\n\<connectioninfoitem\> .*\n/
|
||||
assert capture_io(fn -> @command.run(["list_channels"], %{}) end) =~ ~r/\n\<channelinfoitem\> .*\n/
|
||||
assert @command.run(["list_vhosts"], %{}) =~ ~r/\n\<vhostinfoitem\> .*/
|
||||
assert @command.run(["list_queues"], %{}) =~ ~r/\n\<queueinfoitem\> .*/
|
||||
assert @command.run(["list_exchanges"], %{}) =~ ~r/\n\<exchangeinfoitem\> .*/
|
||||
assert @command.run(["list_bindings"], %{}) =~ ~r/\n\<bindinginfoitem\> .*/
|
||||
assert @command.run(["list_connections"], %{}) =~ ~r/\n\<connectioninfoitem\> .*/
|
||||
assert @command.run(["list_channels"], %{}) =~ ~r/\n\<channelinfoitem\> .*/
|
||||
end
|
||||
|
||||
test "No arguments also produce help command" do
|
||||
assert capture_io(fn -> @command.run([], %{}) end) =~ ~r/Usage:/
|
||||
assert @command.run([], %{}) =~ ~r/Usage:/
|
||||
end
|
||||
|
||||
test "Extra arguments also produce help command" do
|
||||
assert capture_io(fn -> @command.run(["extra1", "extra2"], %{}) end) =~ ~r/Usage:/
|
||||
end
|
||||
assert @command.run(["extra1", "extra2"], %{}) =~ ~r/Usage:/
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ defmodule HelpersTest do
|
|||
import TestHelper
|
||||
|
||||
@subject RabbitMQ.CLI.Ctl.Helpers
|
||||
|
||||
|
||||
setup_all do
|
||||
RabbitMQ.CLI.Distribution.start()
|
||||
on_exit([], fn -> :net_kernel.stop() end)
|
||||
|
|
|
|||
|
|
@ -69,17 +69,17 @@ defmodule JoinClusterCommandTest do
|
|||
test "run: joining self is invalid", context do
|
||||
stop_rabbitmq_app
|
||||
assert match?(
|
||||
{:join_cluster_failed, {:cannot_cluster_node_with_itself, _}},
|
||||
{:error, :cannot_cluster_node_with_itself},
|
||||
@command.run([context[:opts][:node]], context[:opts]))
|
||||
start_rabbitmq_app
|
||||
end
|
||||
|
||||
# TODO
|
||||
#test "run: request to an active node fails", context do
|
||||
# assert match?(
|
||||
# {:join_cluster_failed, {:mnesia_unexpectedly_running, _}},
|
||||
# @command.run([context[:opts][:node]], context[:opts]))
|
||||
#end
|
||||
test "run: request to an active node fails", context do
|
||||
assert match?(
|
||||
{:error, :mnesia_unexpectedly_running},
|
||||
@command.run([context[:opts][:node]], context[:opts]))
|
||||
end
|
||||
|
||||
test "run: request to a non-existent node returns nodedown", context do
|
||||
target = :jake@thedog
|
||||
|
|
@ -109,4 +109,12 @@ defmodule JoinClusterCommandTest do
|
|||
assert @command.banner(["a"], context[:opts]) =~
|
||||
~r/Clustering node #{get_rabbit_hostname} with a/
|
||||
end
|
||||
|
||||
test "output mnesia is running error", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code,
|
||||
"Mnesia is still running on node " <> _},
|
||||
@command.output({:error, :mnesia_unexpectedly_running}, context[:opts]))
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ defmodule ListBindingsCommandTest do
|
|||
default_keys = ~w(source_name source_kind destination_name destination_kind routing_key arguments)
|
||||
declare_queue("test_queue", @vhost)
|
||||
:timer.sleep(100)
|
||||
|
||||
|
||||
{keys, _} = @command.merge_defaults([], context[:opts])
|
||||
assert default_keys == keys
|
||||
assert default_keys == keys
|
||||
end
|
||||
|
||||
test "validate: returns bad_info_key on a single bad arg", context do
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ defmodule ListChannelsCommandTest do
|
|||
with_channel("/", fn(_channel1) ->
|
||||
with_channel("/", fn(_channel2) ->
|
||||
all_channels = run_command_to_list(@command, [["pid", "user", "connection"], context[:opts]])
|
||||
channels = Enum.filter(all_channels,
|
||||
channels = Enum.filter(all_channels,
|
||||
fn(ch) ->
|
||||
not Enum.member?(existent_channels, ch[:pid])
|
||||
end)
|
||||
|
|
@ -105,13 +105,13 @@ defmodule ListChannelsCommandTest do
|
|||
{:ok, _} = AMQP.Channel.open(conn)
|
||||
{:ok, _} = AMQP.Channel.open(conn)
|
||||
all_channels = run_command_to_list(@command, [["pid", "user", "connection"], context[:opts]])
|
||||
channels = Enum.filter(all_channels,
|
||||
channels = Enum.filter(all_channels,
|
||||
fn(ch) ->
|
||||
not Enum.member?(existent_channels, ch[:pid])
|
||||
end)
|
||||
|
||||
chan1 = Enum.at(channels, 0)
|
||||
chan2 = Enum.at(channels, 1)
|
||||
chan2 = Enum.at(channels, 1)
|
||||
assert Keyword.keys(chan1) == ~w(pid user connection)a
|
||||
assert Keyword.keys(chan2) == ~w(pid user connection)a
|
||||
assert "guest" == chan1[:user]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
defmodule ListConnectionsCommandTest do
|
||||
use ExUnit.Case, async: false
|
||||
import TestHelper
|
||||
|
||||
|
||||
@command RabbitMQ.CLI.Ctl.Commands.ListConnectionsCommand
|
||||
@user "guest"
|
||||
@default_timeout 15000
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ defmodule ListUsersCommandTest do
|
|||
|
||||
@tag test_timeout: 0
|
||||
test "run: timeout causes command to return a bad RPC", context do
|
||||
assert @command.run([], context[:opts]) ==
|
||||
assert @command.run([], context[:opts]) ==
|
||||
{:badrpc, :timeout}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ defmodule EnablePluginsCommandTest do
|
|||
enabled: [:amqp_client, :rabbitmq_metronome]} ==
|
||||
@command.run(["rabbitmq_metronome"], context[:opts])
|
||||
assert {:ok, [xs]} = :file.consult(context[:opts][:enabled_plugins_file])
|
||||
|
||||
|
||||
assert_equal_sets([:rabbitmq_metronome], xs)
|
||||
assert_equal_sets([:amqp_client, :rabbitmq_metronome], currently_active_plugins(context))
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ defmodule SetPluginsCommandTest do
|
|||
end)
|
||||
|
||||
:erlang.disconnect_node(node)
|
||||
:net_kernel.stop()
|
||||
# :net_kernel.stop()
|
||||
|
||||
{:ok, opts: opts}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ defmodule RabbitMQCtlTest do
|
|||
command = ["authenticate_user", "kirk", "makeitso"]
|
||||
assert capture_io(
|
||||
fn -> error_check(command, exit_dataerr)
|
||||
end) =~ ~r/Error: failed to authenticate user "kirk"/
|
||||
end) =~ ~r/Error: failed to authenticate user \"kirk\"/
|
||||
delete_user "kirk"
|
||||
end
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ defmodule RabbitMQCtlTest do
|
|||
command1 = ["--invalid=true", "list_permissions", "-p", "/"]
|
||||
assert capture_io(fn ->
|
||||
error_check(command1, exit_usage)
|
||||
end) =~ ~r/Error: invalid options for this command/
|
||||
end) =~ ~r/Error: Invalid options for this command/
|
||||
|
||||
command2 = ["--node", "rabbit", "status", "quack"]
|
||||
assert capture_io(fn ->
|
||||
|
|
@ -155,11 +155,11 @@ defmodule RabbitMQCtlTest do
|
|||
command1 = ["status", "--nod=rabbit"]
|
||||
assert capture_io(fn ->
|
||||
error_check(command1, exit_usage)
|
||||
end) =~ ~r/Error: invalid options for this command/
|
||||
end) =~ ~r/Error: Invalid options for this command/
|
||||
|
||||
command2 = ["list_permissions", "-o", "/"]
|
||||
assert capture_io(fn ->
|
||||
error_check(command2, exit_usage)
|
||||
end) =~ ~r/Error: invalid options for this command/
|
||||
end) =~ ~r/Error: Invalid options for this command/
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ defmodule ResetCommandTest do
|
|||
test "run: reset request to an active node with a stopped rabbit app succeeds", context do
|
||||
add_vhost "some_vhost"
|
||||
#ensure the vhost really does exist
|
||||
assert vhost_exists? "some_vhost"
|
||||
assert vhost_exists? "some_vhost"
|
||||
stop_rabbitmq_app
|
||||
assert :ok == @command.run([], context[:opts])
|
||||
start_rabbitmq_app
|
||||
|
|
@ -56,9 +56,9 @@ defmodule ResetCommandTest do
|
|||
|
||||
test "run: reset request to an active node with a running rabbit app fails", context do
|
||||
add_vhost "some_vhost"
|
||||
assert vhost_exists? "some_vhost"
|
||||
assert match?({:reset_failed, {:mnesia_unexpectedly_running, _}}, @command.run([], context[:opts]))
|
||||
assert vhost_exists? "some_vhost"
|
||||
assert vhost_exists? "some_vhost"
|
||||
assert match?({:error, :mnesia_unexpectedly_running}, @command.run([], context[:opts]))
|
||||
assert vhost_exists? "some_vhost"
|
||||
end
|
||||
|
||||
test "run: request to a non-existent node returns nodedown" do
|
||||
|
|
@ -71,4 +71,12 @@ defmodule ResetCommandTest do
|
|||
test "banner", context do
|
||||
assert @command.banner([], context[:opts]) =~ ~r/Resetting node #{get_rabbit_hostname}/
|
||||
end
|
||||
|
||||
test "output mnesia is running error", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code,
|
||||
"Mnesia is still running on node " <> _},
|
||||
@command.output({:error, :mnesia_unexpectedly_running}, context[:opts]))
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ defmodule RotateLogsCommandTest do
|
|||
end
|
||||
|
||||
test "run: request to a named, active node succeeds", context do
|
||||
assert @command.run([], context[:opts]) == :ok
|
||||
assert @command.run([], context[:opts]) == :ok
|
||||
end
|
||||
|
||||
test "run: request to a non-existent node returns nodedown" do
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
## The contents of this file are subject to the Mozilla Public License
|
||||
## Version 1.1 (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License
|
||||
## at http://www.mozilla.org/MPL/
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS"
|
||||
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
## the License for the specific language governing rights and
|
||||
## limitations under the License.
|
||||
##
|
||||
## The Original Code is RabbitMQ.
|
||||
##
|
||||
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
## Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
|
||||
defmodule StandardCodesTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
alias RabbitMQ.CLI.StandardCodes, as: StandardCodes
|
||||
|
||||
test "An :ok is unchanged" do
|
||||
assert_unchanged :ok
|
||||
assert_unchanged {:ok, "input"}
|
||||
end
|
||||
|
||||
test "A node_down is unchanged" do
|
||||
assert_unchanged {:badrpc, :nodedown}
|
||||
end
|
||||
|
||||
test "A time_out is unchanged" do
|
||||
assert_unchanged {:badrpc, :timeout}
|
||||
end
|
||||
|
||||
test "A refused is unchanged" do
|
||||
assert_unchanged {:refused, "name", "string", []}
|
||||
end
|
||||
|
||||
test "A validation failure is unchanged" do
|
||||
assert_unchanged {:validation_failure, :bad_argument}
|
||||
assert_unchanged {:validation_failure, :too_many_args}
|
||||
assert_unchanged {:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
test "A properly-tagged error is unchanged" do
|
||||
assert_unchanged {:error, {:reason, "excuse"}}
|
||||
end
|
||||
|
||||
test "An unidentified atom is converted to an error" do
|
||||
assert_error_wrapped :unknown_symbol
|
||||
end
|
||||
|
||||
test "A tuple beginning with an unidentified atom is converted to an error" do
|
||||
assert_error_wrapped {:error_string, "explanation"}
|
||||
end
|
||||
|
||||
test "A data return is converted to {:ok, <data>}" do
|
||||
assert StandardCodes.map_to_standard_code(["sandwich"]) == {:ok, ["sandwich"]}
|
||||
end
|
||||
|
||||
test "A 'bad option' message is unchanged" do
|
||||
assert_unchanged {:bad_option, [:sandwich]}
|
||||
end
|
||||
|
||||
|
||||
defp assert_unchanged(input) do
|
||||
assert StandardCodes.map_to_standard_code(input) == input
|
||||
end
|
||||
|
||||
defp assert_error_wrapped(input) do
|
||||
assert StandardCodes.map_to_standard_code(input) == {:error, input}
|
||||
end
|
||||
end
|
||||
|
|
@ -54,7 +54,7 @@ defmodule UpdateClusterNodesCommandTest do
|
|||
test "run: specifying self as seed node fails validation", context do
|
||||
stop_rabbitmq_app
|
||||
assert match?(
|
||||
{:join_cluster_failed, {:cannot_cluster_node_with_itself, _}},
|
||||
{:error, :cannot_cluster_node_with_itself},
|
||||
@command.run([context[:opts][:node]], context[:opts]))
|
||||
start_rabbitmq_app
|
||||
end
|
||||
|
|
@ -85,4 +85,12 @@ defmodule UpdateClusterNodesCommandTest do
|
|||
assert @command.banner(["a"], context[:opts]) =~
|
||||
~r/Will seed #{get_rabbit_hostname} from a on next start/
|
||||
end
|
||||
|
||||
test "output mnesia is running error", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code,
|
||||
"Mnesia is still running on node " <> _},
|
||||
@command.output({:error, :mnesia_unexpectedly_running}, context[:opts]))
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ defmodule WaitCommandTest do
|
|||
|
||||
on_exit([], fn ->
|
||||
:erlang.disconnect_node(get_rabbit_hostname)
|
||||
:net_kernel.stop()
|
||||
# :net_kernel.stop()
|
||||
end)
|
||||
|
||||
:ok
|
||||
|
|
@ -44,4 +44,32 @@ defmodule WaitCommandTest do
|
|||
test "banner", context do
|
||||
assert @command.banner([], context[:opts]) =~ ~r/Waiting for node #{get_rabbit_hostname}/
|
||||
end
|
||||
|
||||
test "output: process not running error", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code, "Error: process is not running."},
|
||||
@command.output({:error, :process_not_running}, context[:opts]))
|
||||
end
|
||||
|
||||
test "output: garbage in pid file error", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code, "Error: garbage in pid file."},
|
||||
@command.output({:error, {:garbage_in_pid_file, "somefile"}}, context[:opts]))
|
||||
end
|
||||
|
||||
test "output: could not read pid error", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code, "Error: could not read pid. Detail: something wrong"},
|
||||
@command.output({:error, {:could_not_read_pid, "something wrong"}}, context[:opts]))
|
||||
end
|
||||
|
||||
test "output: default output is fine", context do
|
||||
exit_code = RabbitMQ.CLI.ExitCodes.exit_software
|
||||
assert match?({:error, ^exit_code, "Error:\nmessage"}, @command.output({:error, "message"}, context[:opts]))
|
||||
assert match?({:error, ^exit_code, "Error:\nmessage"}, @command.output({:error, :message}, context[:opts]))
|
||||
assert match?({:error, ^exit_code, "Error:\nmessage"}, @command.output(:message, context[:opts]))
|
||||
assert match?({:ok, "ok"}, @command.output({:ok, "ok"}, context[:opts]))
|
||||
assert match?(:ok, @command.output(:ok, context[:opts]))
|
||||
assert match?({:ok, "ok"}, @command.output("ok", context[:opts]))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue