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-06-09 09:00:43 +08:00
|
|
|
alias RabbitMQ.CLI.Distribution, as: Distribution
|
2016-06-09 08:34:56 +08:00
|
|
|
|
2016-06-10 07:54:22 +08:00
|
|
|
alias RabbitMQ.CLI.Ctl.Commands.HelpCommand, as: HelpCommand
|
2016-08-22 19:58:37 +08:00
|
|
|
alias RabbitMQ.CLI.Output, as: Output
|
2016-11-04 19:11:03 +08:00
|
|
|
alias RabbitMQ.CLI.ExitCodes, as: ExitCodes
|
2016-06-10 07:54:22 +08:00
|
|
|
|
2016-06-09 09:08:21 +08:00
|
|
|
import RabbitMQ.CLI.Ctl.Helpers
|
|
|
|
|
import RabbitMQ.CLI.Ctl.Parser
|
2016-08-22 19:58:37 +08:00
|
|
|
|
2016-08-11 01:19:06 +08:00
|
|
|
def main(["--auto-complete", "./rabbitmqctl " <> str]) do
|
2016-07-29 00:46:37 +08:00
|
|
|
auto_complete(str)
|
|
|
|
|
end
|
2016-08-11 01:19:06 +08:00
|
|
|
def main(["--auto-complete", "rabbitmqctl " <> str]) do
|
2016-07-29 00:46:37 +08:00
|
|
|
auto_complete(str)
|
|
|
|
|
end
|
2016-05-09 18:07:46 +08:00
|
|
|
def main(unparsed_command) do
|
2016-11-04 02:10:18 +08:00
|
|
|
{parsed_cmd, parsed_options, invalid} = parse(unparsed_command)
|
2016-08-18 21:56:21 +08:00
|
|
|
|
2016-06-09 08:34:56 +08:00
|
|
|
case {is_command?(parsed_cmd), invalid} do
|
2016-07-29 00:46:37 +08:00
|
|
|
## No such command
|
2016-06-08 22:19:37 +08:00
|
|
|
{false, _} ->
|
2016-08-22 19:58:37 +08:00
|
|
|
usage_string = HelpCommand.all_usage()
|
2016-11-04 19:11:03 +08:00
|
|
|
{:error, ExitCodes.exit_usage, usage_string};
|
2016-07-29 00:46:37 +08:00
|
|
|
## Invalid options
|
2016-06-08 22:19:37 +08:00
|
|
|
{_, [_|_]} ->
|
2016-11-04 19:11:03 +08:00
|
|
|
error = validation_error({:bad_option, invalid}, unparsed_command);
|
2016-07-29 00:46:37 +08:00
|
|
|
## Command valid
|
2016-06-08 22:19:37 +08:00
|
|
|
{true, []} ->
|
2016-11-04 02:10:18 +08:00
|
|
|
options = parsed_options |> merge_all_defaults |> normalize_node
|
|
|
|
|
Distribution.start(options)
|
|
|
|
|
|
2016-11-04 19:11:03 +08:00
|
|
|
[command_name | arguments] = parsed_cmd
|
|
|
|
|
command = commands[command_name]
|
|
|
|
|
case invalid_flags(command, options) do
|
|
|
|
|
[] ->
|
|
|
|
|
case run_command(options, command, arguments) do
|
|
|
|
|
{:error, _, _} = err ->
|
|
|
|
|
err;
|
|
|
|
|
{:validation_failure, err} ->
|
|
|
|
|
validation_error(err, unparsed_command);
|
|
|
|
|
output ->
|
|
|
|
|
formatter = get_formatter(command, options)
|
|
|
|
|
printer = get_printer(options)
|
|
|
|
|
|
|
|
|
|
output
|
|
|
|
|
|> Output.format_output(formatter, options)
|
|
|
|
|
|> Output.print_output(printer, options)
|
|
|
|
|
end;
|
|
|
|
|
result ->
|
|
|
|
|
error = validation_error({:bad_option, result}, unparsed_command);
|
2016-11-04 02:10:18 +08:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|> handle_shutdown
|
|
|
|
|
end
|
2016-06-08 22:19:37 +08:00
|
|
|
|
2016-11-04 02:10:18 +08:00
|
|
|
def handle_shutdown({:error, exit_code, output}) do
|
2016-11-04 19:11:03 +08:00
|
|
|
output_device = case exit_code == ExitCodes.exit_ok do
|
2016-11-04 19:12:14 +08:00
|
|
|
true -> :stdio;
|
2016-11-04 19:11:03 +08:00
|
|
|
false -> :stderr
|
|
|
|
|
end
|
2016-11-04 02:10:18 +08:00
|
|
|
for line <- List.flatten([output]) do
|
2016-11-04 19:11:03 +08:00
|
|
|
IO.puts(output_device, line)
|
2016-03-05 05:35:54 +08:00
|
|
|
end
|
2016-11-04 02:10:18 +08:00
|
|
|
exit_program(exit_code)
|
|
|
|
|
end
|
|
|
|
|
def handle_shutdown(_) do
|
2016-11-04 19:11:03 +08:00
|
|
|
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
|
|
|
|
|
AutoComplete.complete(str)
|
|
|
|
|
|> Stream.map(&IO.puts/1) |> Stream.run
|
2016-11-04 19:11:03 +08:00
|
|
|
exit_program(ExitCodes.exit_ok)
|
2016-07-29 00:46:37 +08:00
|
|
|
end
|
|
|
|
|
|
2016-08-09 02:43:09 +08:00
|
|
|
def merge_all_defaults(%{} = options) do
|
2016-03-08 03:08:38 +08:00
|
|
|
options
|
2016-05-24 19:33:11 +08:00
|
|
|
|> merge_defaults_node
|
|
|
|
|
|> merge_defaults_timeout
|
2016-06-08 22:19:37 +08:00
|
|
|
|> merge_defaults_longnames
|
2016-03-08 03:08:38 +08:00
|
|
|
end
|
|
|
|
|
|
2016-05-24 19:33:11 +08:00
|
|
|
defp merge_defaults_node(%{} = opts), do: Map.merge(%{node: get_rabbit_hostname}, opts)
|
2016-03-08 06:45:50 +08:00
|
|
|
|
2016-05-24 19:33:11 +08:00
|
|
|
defp merge_defaults_timeout(%{} = opts), do: Map.merge(%{timeout: :infinity}, opts)
|
2016-03-08 06:45:50 +08:00
|
|
|
|
2016-06-08 22:19:37 +08:00
|
|
|
defp merge_defaults_longnames(%{} = opts), do: Map.merge(%{longnames: false}, opts)
|
|
|
|
|
|
2016-06-10 05:55:59 +08:00
|
|
|
defp normalize_node(%{node: node} = opts) do
|
|
|
|
|
Map.merge(opts, %{node: parse_node(node)})
|
|
|
|
|
end
|
|
|
|
|
|
2016-11-04 19:11:03 +08:00
|
|
|
defp maybe_connect_to_rabbitmq(HelpCommand, _), do: nil
|
2016-05-31 16:40:48 +08:00
|
|
|
defp maybe_connect_to_rabbitmq(_, node) do
|
2016-06-09 08:34:56 +08:00
|
|
|
connect_to_rabbitmq(node)
|
2016-05-31 16:40:48 +08:00
|
|
|
end
|
|
|
|
|
|
2016-11-04 19:11:03 +08:00
|
|
|
defp run_command(options, command, arguments) do
|
|
|
|
|
{arguments, options} = command.merge_defaults(arguments, options)
|
|
|
|
|
case command.validate(arguments, options) do
|
|
|
|
|
:ok ->
|
|
|
|
|
print_banner(command, arguments, options)
|
|
|
|
|
maybe_connect_to_rabbitmq(command, options[:node])
|
|
|
|
|
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()]))}
|
2016-11-04 02:10:18 +08:00
|
|
|
end
|
2016-11-04 19:11:03 +08:00
|
|
|
{:validation_failure, _} = err -> err
|
2016-11-04 02:10:18 +08:00
|
|
|
end
|
2016-02-03 02:54:36 +08:00
|
|
|
end
|
2016-02-26 05:16:25 +08:00
|
|
|
|
2016-11-04 02:10:18 +08:00
|
|
|
defp get_formatter(command, %{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(command)
|
|
|
|
|
end
|
|
|
|
|
end
|
2016-11-04 19:11:03 +08:00
|
|
|
defp get_formatter(command, _) do
|
2016-11-04 02:10:18 +08:00
|
|
|
default_formatter(command)
|
|
|
|
|
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 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
|
2016-05-05 06:31:11 +08:00
|
|
|
end
|
2016-04-27 02:38:23 +08:00
|
|
|
|
2016-05-24 19:33:11 +08:00
|
|
|
defp print_banner(command, args, opts) do
|
2016-05-31 16:40:48 +08:00
|
|
|
case command.banner(args, opts) do
|
2016-06-02 06:38:09 +08:00
|
|
|
nil -> nil
|
2016-05-31 16:40:48 +08:00
|
|
|
banner -> IO.inspect banner
|
|
|
|
|
end
|
2016-05-24 19:33:11 +08:00
|
|
|
end
|
|
|
|
|
|
2016-11-04 19:11:03 +08:00
|
|
|
defp validation_error(err_detail, unparsed_command) do
|
2016-05-30 07:16:10 +08:00
|
|
|
{[command_name | _], _, _} = parse(unparsed_command)
|
2016-08-19 00:38:49 +08:00
|
|
|
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(" ")}"
|
2016-05-30 07:16:10 +08:00
|
|
|
|
2016-08-22 19:58:37 +08:00
|
|
|
usage = case is_command?(command_name) do
|
2016-05-30 07:16:10 +08:00
|
|
|
true ->
|
2016-06-09 08:34:56 +08:00
|
|
|
command = commands[command_name]
|
2016-08-22 19:58:37 +08:00
|
|
|
HelpCommand.base_usage(HelpCommand.program_name(), command)
|
2016-05-30 07:16:10 +08:00
|
|
|
false ->
|
2016-08-22 19:58:37 +08:00
|
|
|
HelpCommand.all_usage()
|
2016-05-30 07:16:10 +08:00
|
|
|
end
|
2016-08-22 19:58:37 +08:00
|
|
|
|
2016-11-04 19:11:03 +08:00
|
|
|
message = base_error <> "\n" <> usage
|
|
|
|
|
{:error, ExitCodes.exit_code_for({:validation_failure, err_detail}), message}
|
2016-04-02 01:58:47 +08:00
|
|
|
end
|
|
|
|
|
|
2016-08-19 00:38:49 +08:00
|
|
|
defp format_validation_error(:not_enough_args, _), do: "not enough arguments."
|
|
|
|
|
defp format_validation_error({:not_enough_args, detail}, _), do: "not enough arguments. #{detail}"
|
|
|
|
|
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
|
|
|
|
|
Enum.join([header | for {key, val} <- opts do "#{key} : #{val}" end], "\n")
|
2016-08-18 21:56:21 +08:00
|
|
|
end
|
2016-08-22 19:58:37 +08:00
|
|
|
defp format_validation_error(err, _), do: inspect err
|
2016-03-23 00:21:12 +08:00
|
|
|
|
2016-05-05 18:39:27 +08:00
|
|
|
defp invalid_flags(command, opts) do
|
2016-08-18 21:56:21 +08:00
|
|
|
Map.take(opts, Map.keys(opts) -- (command.flags ++ global_flags))
|
|
|
|
|
|> Map.to_list
|
2016-05-05 03:40:09 +08:00
|
|
|
end
|
|
|
|
|
|
2016-03-23 00:21:12 +08:00
|
|
|
defp exit_program(code) do
|
|
|
|
|
:net_kernel.stop
|
2016-04-02 00:13:01 +08:00
|
|
|
exit({:shutdown, code})
|
2016-03-23 00:21:12 +08:00
|
|
|
end
|
2016-02-03 02:54:36 +08:00
|
|
|
end
|