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

244 lines
8.0 KiB
Elixir
Raw Normal View History

2016-02-03 23:01:32 +08:00
## 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.
2016-02-03 22:42:45 +08:00
defmodule RabbitMQCtl do
2016-11-15 01:52:08 +08:00
alias RabbitMQ.CLI.Core.Distribution, as: Distribution
alias RabbitMQ.CLI.Ctl.Commands.HelpCommand, as: HelpCommand
2016-11-15 01:52:08 +08:00
alias RabbitMQ.CLI.Core.Output, as: Output
alias RabbitMQ.CLI.Core.ExitCodes, as: ExitCodes
alias RabbitMQ.CLI.Core.CommandModules, as: CommandModules
alias RabbitMQ.CLI.Core.Parser, as: Parser
2016-11-15 01:52:08 +08:00
import RabbitMQ.CLI.Core.Helpers
2016-08-22 19:58:37 +08:00
def main(["--auto-complete", "./rabbitmqctl " <> str]) do
2016-07-29 00:46:37 +08:00
auto_complete(str)
end
def main(["--auto-complete", "rabbitmqctl " <> str]) do
2016-07-29 00:46:37 +08:00
auto_complete(str)
end
def main(unparsed_command) do
{parsed_cmd, parsed_options, _} = Parser.parse_global(unparsed_command)
global_options = parsed_options |> merge_all_defaults |> normalize_options
CommandModules.load(global_options)
case get_command_and_arguments(parsed_cmd) do
:no_command ->
{:error, ExitCodes.exit_usage, HelpCommand.all_usage()};
{command_name, command, arguments} ->
case Parser.parse_command_specific(command, unparsed_command) do
{^parsed_cmd, _, [_|_] = invalid} ->
validation_error({:bad_option, invalid}, command_name, unparsed_command);
{^parsed_cmd, command_options, []} ->
## Merge normalized global options
options = Map.merge(command_options, global_options)
Distribution.start(options)
case execute_command(command, arguments, options) do
{:error, _, _} = err ->
err;
{:validation_failure, err} ->
validation_error(err, command_name, unparsed_command);
output ->
formatter = get_formatter(command, options)
printer = get_printer(options)
output
|> Output.format_output(formatter, options)
|> Output.print_output(printer, options)
end
2016-11-04 02:10:18 +08:00
end
end
|> handle_shutdown
end
2016-06-08 22:19:37 +08:00
defp get_command_and_arguments([]) do
:no_command
end
defp get_command_and_arguments([command_name | arguments]) do
case CommandModules.module_map[command_name] do
nil -> :no_command;
command when is_atom(command) -> {command_name, command, arguments}
end
end
2016-11-04 02:10:18 +08:00
def handle_shutdown({:error, exit_code, output}) do
output_device = case exit_code == ExitCodes.exit_ok do
2016-11-04 19:12:14 +08:00
true -> :stdio;
false -> :stderr
end
2016-11-04 02:10:18 +08:00
for line <- List.flatten([output]) do
IO.puts(output_device, line)
end
2016-11-04 02:10:18 +08:00
exit_program(exit_code)
end
def handle_shutdown(_) do
exit_program(ExitCodes.exit_ok)
2016-02-03 05:32:35 +08:00
end
2016-07-29 00:46:37 +08:00
def auto_complete(str) do
2016-11-15 01:52:08 +08:00
Rabbitmq.CLI.AutoComplete.complete(str)
2016-07-29 00:46:37 +08:00
|> Stream.map(&IO.puts/1) |> Stream.run
exit_program(ExitCodes.exit_ok)
2016-07-29 00:46:37 +08:00
end
def merge_all_defaults(%{} = options) do
options
|> merge_defaults_node
|> merge_defaults_timeout
2016-06-08 22:19:37 +08:00
|> merge_defaults_longnames
end
defp merge_defaults_node(%{} = opts), do: Map.merge(%{node: get_rabbit_hostname}, opts)
2016-03-08 06:45:50 +08:00
defp merge_defaults_timeout(%{} = opts), do: Map.merge(%{timeout: :infinity}, opts)
2016-03-08 06:45:50 +08:00
2016-06-08 22:19:37 +08:00
defp merge_defaults_longnames(%{} = opts), do: Map.merge(%{longnames: false}, opts)
2016-11-11 01:40:43 +08:00
defp normalize_options(opts) do
opts
|> normalize_node
|> normalize_timeout
end
defp normalize_node(%{node: node} = opts) do
Map.merge(opts, %{node: parse_node(node)})
end
2016-11-11 01:40:43 +08:00
defp normalize_timeout(%{timeout: timeout} = opts)
when is_integer(timeout) do
Map.put(opts, :timeout, timeout * 1000)
end
defp normalize_timeout(opts) do
opts
end
defp maybe_connect_to_rabbitmq(HelpCommand, _), do: nil
defp maybe_connect_to_rabbitmq(_, node) do
connect_to_rabbitmq(node)
end
defp execute_command(command, arguments, options) do
{arguments, options} = command.merge_defaults(arguments, options)
case command.validate(arguments, options) do
:ok ->
2016-10-26 23:25:39 +08:00
maybe_print_banner(command, arguments, options)
maybe_connect_to_rabbitmq(command, options[:node])
maybe_run_command(command, arguments, options)
{:validation_failure, _} = err -> err
2016-11-04 02:10:18 +08:00
end
2016-02-03 02:54:36 +08:00
end
defp maybe_run_command(_, _, %{dry_run: true}) do
:ok
end
defp maybe_run_command(command, arguments, options) do
try do
command.run(arguments, options) |> command.output(options)
catch _error_type, error ->
{:error, ExitCodes.exit_software,
to_string(:io_lib.format("Error: ~n~p~n Stacktrace ~p~n",
[error, System.stacktrace()]))}
end
end
2016-11-04 02:10:18 +08:00
defp get_formatter(command, %{formatter: formatter}) do
2016-11-16 00:14:22 +08:00
module_name = Module.safe_concat("RabbitMQ.CLI.Formatters", Macro.camelize(formatter))
2016-11-04 02:10:18 +08:00
case Code.ensure_loaded(module_name) do
{:module, _} -> module_name;
{:error, :nofile} -> default_formatter(command)
end
end
defp get_formatter(command, _) do
2016-11-04 02:10:18 +08:00
default_formatter(command)
end
def get_printer(%{printer: printer}) do
2016-11-16 00:14:22 +08:00
module_name = String.to_atom("RabbitMQ.CLI.Printers." <> Macro.camelize(printer))
2016-11-04 02:10:18 +08:00
case Code.ensure_loaded(module_name) do
{:module, _} -> module_name;
{:error, :nofile} -> default_printer
end
end
def get_printer(_) do
default_printer
end
def default_printer() do
RabbitMQ.CLI.Printers.StdIO
end
def default_formatter(command) do
case function_exported?(command, :formatter, 0) do
true -> command.formatter;
false -> RabbitMQ.CLI.Formatters.Inspect
end
end
2016-10-26 23:25:39 +08:00
## Suppress banner if --quiet option is provided
defp maybe_print_banner(_, _, %{quiet: true}) do
nil
end
defp maybe_print_banner(command, args, opts) do
case command.banner(args, opts) do
2016-10-26 23:25:39 +08:00
nil -> nil
banner ->
case banner do
list when is_list(list) ->
for line <- list, do: IO.puts(line);
binary when is_binary(binary) ->
IO.puts(binary)
end
end
end
defp validation_error(err_detail, command_name, unparsed_command) do
err = format_validation_error(err_detail, command_name) # TODO format the error better
2016-08-22 19:58:37 +08:00
base_error = "Error: #{err}\nGiven:\n\t#{unparsed_command |> Enum.join(" ")}"
usage = case CommandModules.is_command?(command_name) do
true ->
command = CommandModules.module_map[command_name]
2016-08-22 19:58:37 +08:00
HelpCommand.base_usage(HelpCommand.program_name(), command)
false ->
2016-08-22 19:58:37 +08:00
HelpCommand.all_usage()
end
message = base_error <> "\n" <> usage
{:error, ExitCodes.exit_code_for({:validation_failure, err_detail}), message}
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({:bad_option, opts}, command_name) do
header = case CommandModules.is_command?(command_name) do
true -> "Invalid options for this command:";
false -> "Invalid options:"
end
Enum.join([header | for {key, val} <- opts do "#{key} : #{val}" end], "\n")
end
2016-08-22 19:58:37 +08:00
defp format_validation_error(err, _), do: inspect err
defp exit_program(code) do
:net_kernel.stop
exit({:shutdown, code})
end
2016-02-03 02:54:36 +08:00
end