summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/rabbit_control.erl71
-rw-r--r--src/rabbit_misc.erl102
-rw-r--r--src/rabbit_plugins.erl30
-rw-r--r--src/rabbit_tests.erl81
4 files changed, 204 insertions, 80 deletions
diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl
index 2dea2a2f..2a84f7b2 100644
--- a/src/rabbit_control.erl
+++ b/src/rabbit_control.erl
@@ -26,6 +26,61 @@
-define(NODE_OPT, "-n").
-define(VHOST_OPT, "-p").
+-define(QUIET_DEF, {?QUIET_OPT, flag}).
+-define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}).
+-define(VHOST_DEF, {?VHOST_OPT, {option, "/"}}).
+
+-define(GLOBAL_DEFS(Node), [?QUIET_DEF, ?NODE_DEF(Node)]).
+
+-define(COMMANDS,
+ [stop,
+ stop_app,
+ start_app,
+ wait,
+ reset,
+ force_reset,
+ rotate_logs,
+
+ cluster,
+ force_cluster,
+ cluster_status,
+
+ add_user,
+ delete_user,
+ change_password,
+ clear_password,
+ set_user_tags,
+ list_users,
+
+ add_vhost,
+ delete_vhost,
+ list_vhosts,
+ {set_permissions, [?VHOST_DEF]},
+ {clear_permissions, [?VHOST_DEF]},
+ {list_permissions, [?VHOST_DEF]},
+ {list_user_permissions, [?VHOST_DEF]},
+
+ set_parameter,
+ clear_parameter,
+ list_parameters,
+
+ {list_queues, [?VHOST_DEF]},
+ {list_exchanges, [?VHOST_DEF]},
+ {list_bindings, [?VHOST_DEF]},
+ {list_connections, [?VHOST_DEF]},
+ list_channels,
+ {list_consumers, [?VHOST_DEF]},
+ status,
+ environment,
+ report,
+ eval,
+
+ close_connection,
+ {trace_on, [?VHOST_DEF]},
+ {trace_off, [?VHOST_DEF]},
+ set_vm_memory_high_watermark
+ ]).
+
-define(GLOBAL_QUERIES,
[{"Connections", rabbit_networking, connection_info_all,
connection_info_keys},
@@ -57,19 +112,18 @@
start() ->
{ok, [[NodeStr|_]|_]} = init:get_argument(nodename),
- {[Command0 | Args], Opts} =
- case rabbit_misc:get_options([{flag, ?QUIET_OPT},
- {option, ?NODE_OPT, NodeStr},
- {option, ?VHOST_OPT, "/"}],
- init:get_plain_arguments()) of
- {[], _Opts} -> usage();
- CmdArgsAndOpts -> CmdArgsAndOpts
+ {Command, Opts, Args} =
+ case rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS(NodeStr),
+ init:get_plain_arguments())
+ of
+ {ok, Res} -> Res;
+ no_command -> print_error("could not recognise command", []),
+ usage()
end,
Opts1 = [case K of
?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)};
_ -> {K, V}
end || {K, V} <- Opts],
- Command = list_to_atom(Command0),
Quiet = proplists:get_bool(?QUIET_OPT, Opts1),
Node = proplists:get_value(?NODE_OPT, Opts1),
Inform = case Quiet of
@@ -195,7 +249,6 @@ action(force_cluster, Node, ClusterNodeSs, _Opts, Inform) ->
action(wait, Node, [PidFile], _Opts, Inform) ->
Inform("Waiting for ~p", [Node]),
wait_for_application(Node, PidFile, rabbit, Inform);
-
action(wait, Node, [PidFile, App], _Opts, Inform) ->
Inform("Waiting for ~p on ~p", [App, Node]),
wait_for_application(Node, PidFile, list_to_atom(App), Inform);
diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl
index 706de835..93c784ec 100644
--- a/src/rabbit_misc.erl
+++ b/src/rabbit_misc.erl
@@ -49,7 +49,7 @@
-export([version_compare/2, version_compare/3]).
-export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]).
-export([gb_trees_fold/3, gb_trees_foreach/2]).
--export([get_options/2]).
+-export([parse_arguments/3]).
-export([all_module_attributes/1, build_acyclic_graph/3]).
-export([now_ms/0]).
-export([const_ok/0, const/1]).
@@ -72,7 +72,7 @@
-type(ok_or_error() :: rabbit_types:ok_or_error(any())).
-type(thunk(T) :: fun(() -> T)).
-type(resource_name() :: binary()).
--type(optdef() :: {flag, string()} | {option, string(), any()}).
+-type(optdef() :: flag | {option, string()}).
-type(channel_or_connection_exit()
:: rabbit_types:channel_exit() | rabbit_types:connection_exit()).
-type(digraph_label() :: term()).
@@ -182,8 +182,12 @@
-spec(gb_trees_fold/3 :: (fun ((any(), any(), A) -> A), A, gb_tree()) -> A).
-spec(gb_trees_foreach/2 ::
(fun ((any(), any()) -> any()), gb_tree()) -> 'ok').
--spec(get_options/2 :: ([optdef()], [string()])
- -> {[string()], [{string(), any()}]}).
+-spec(parse_arguments/3 ::
+ ([{atom(), [{string(), optdef()}]} | atom()],
+ [{string(), optdef()}],
+ [string()])
+ -> {'ok', {atom(), [{string(), string()}], [string()]}} |
+ 'no_command').
-spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]).
-spec(build_acyclic_graph/3 ::
(graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}])
@@ -736,39 +740,63 @@ gb_trees_fold1(Fun, Acc, {Key, Val, It}) ->
gb_trees_foreach(Fun, Tree) ->
gb_trees_fold(fun (Key, Val, Acc) -> Fun(Key, Val), Acc end, ok, Tree).
-%% Separate flags and options from arguments.
-%% get_options([{flag, "-q"}, {option, "-p", "/"}],
-%% ["set_permissions","-p","/","guest",
-%% "-q",".*",".*",".*"])
-%% == {["set_permissions","guest",".*",".*",".*"],
-%% [{"-q",true},{"-p","/"}]}
-get_options(Defs, As) ->
- lists:foldl(fun(Def, {AsIn, RsIn}) ->
- {K, {AsOut, V}} =
- case Def of
- {flag, Key} ->
- {Key, get_flag(Key, AsIn)};
- {option, Key, Default} ->
- {Key, get_option(Key, Default, AsIn)}
- end,
- {AsOut, [{K, V} | RsIn]}
- end, {As, []}, Defs).
-
-get_option(K, _Default, [K, V | As]) ->
- {As, V};
-get_option(K, Default, [Nk | As]) ->
- {As1, V} = get_option(K, Default, As),
- {[Nk | As1], V};
-get_option(_, Default, As) ->
- {As, Default}.
-
-get_flag(K, [K | As]) ->
- {As, true};
-get_flag(K, [Nk | As]) ->
- {As1, V} = get_flag(K, As),
- {[Nk | As1], V};
-get_flag(_, []) ->
- {[], false}.
+%% Takes:
+%% * A list of [{atom(), [{string(), optdef()]} | atom()], where the atom()s
+%% are the accepted commands and the optional [string()] is the list of
+%% accepted options for that command
+%% * A list [{string(), optdef()}] of options valid for all commands
+%% * The list of arguments given by the user
+%%
+%% Returns either {ok, {atom(), [{string(), string()}], [string()]} which are
+%% respectively the command, the key-value pairs of the options and the leftover
+%% arguments; or no_command if no command could be parsed.
+parse_arguments(Commands, GlobalDefs, As) ->
+ lists:foldl(maybe_process_opts(GlobalDefs, As), no_command, Commands).
+
+maybe_process_opts(GDefs, As) ->
+ fun({C, Os}, no_command) ->
+ process_opts(atom_to_list(C), dict:from_list(GDefs ++ Os), As);
+ (C, no_command) ->
+ (maybe_process_opts(GDefs, As))({C, []}, no_command);
+ (_, {ok, Res}) ->
+ {ok, Res}
+ end.
+
+process_opts(C, Defs, As0) ->
+ KVs0 = dict:map(fun (_, flag) -> false;
+ (_, {option, V}) -> V
+ end, Defs),
+ process_opts(Defs, C, As0, not_found, KVs0, []).
+
+%% Consume flags/options until you find the correct command. If there are no
+%% arguments or the first argument is not the command we're expecting, fail.
+%% Arguments to this are: definitions, cmd we're looking for, args we
+%% haven't parsed, whether we have found the cmd, options we've found,
+%% plain args we've found.
+process_opts(_Defs, C, [], found, KVs, Outs) ->
+ {ok, {list_to_atom(C), dict:to_list(KVs), lists:reverse(Outs)}};
+process_opts(_Defs, _C, [], not_found, _, _) ->
+ no_command;
+process_opts(Defs, C, [A | As], Found, KVs, Outs) ->
+ OptType = case dict:find(A, Defs) of
+ error -> none;
+ {ok, flag} -> flag;
+ {ok, {option, _}} -> option
+ end,
+ case {OptType, C, Found} of
+ {flag, _, _} -> process_opts(
+ Defs, C, As, Found, dict:store(A, true, KVs),
+ Outs);
+ {option, _, _} -> case As of
+ [] -> no_command;
+ [V | As1] -> process_opts(
+ Defs, C, As1, Found,
+ dict:store(A, V, KVs), Outs)
+ end;
+ {none, A, _} -> process_opts(Defs, C, As, found, KVs, Outs);
+ {none, _, found} -> process_opts(Defs, C, As, found, KVs, [A | Outs]);
+ {none, _, _} -> no_command
+ end.
now_ms() ->
timer:now_diff(now(), {0,0,0}) div 1000.
diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl
index 00880fb2..30c7bb37 100644
--- a/src/rabbit_plugins.erl
+++ b/src/rabbit_plugins.erl
@@ -25,6 +25,18 @@
-define(ENABLED_OPT, "-E").
-define(ENABLED_ALL_OPT, "-e").
+-define(VERBOSE_DEF, {?VERBOSE_OPT, flag}).
+-define(MINIMAL_DEF, {?MINIMAL_OPT, flag}).
+-define(ENABLED_DEF, {?ENABLED_OPT, flag}).
+-define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}).
+
+-define(GLOBAL_DEFS, []).
+
+-define(COMMANDS,
+ [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]},
+ enable,
+ disable]).
+
%%----------------------------------------------------------------------------
-ifdef(use_specs).
@@ -45,16 +57,15 @@ start() ->
{ok, [[PluginsFile|_]|_]} =
init:get_argument(enabled_plugins_file),
{ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir),
- {[Command0 | Args], Opts} =
- case rabbit_misc:get_options([{flag, ?VERBOSE_OPT},
- {flag, ?MINIMAL_OPT},
- {flag, ?ENABLED_OPT},
- {flag, ?ENABLED_ALL_OPT}],
- init:get_plain_arguments()) of
- {[], _Opts} -> usage();
- CmdArgsAndOpts -> CmdArgsAndOpts
+ {Command, Opts, Args} =
+ case rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS,
+ init:get_plain_arguments())
+ of
+ {ok, Res} -> Res;
+ no_command -> print_error("could not recognise command", []),
+ usage()
end,
- Command = list_to_atom(Command0),
+
PrintInvalidCommandError =
fun () ->
print_error("invalid command '~s'",
@@ -396,4 +407,3 @@ report_change() ->
_ ->
ok
end.
-
diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl
index 96b5fa38..bae4928d 100644
--- a/src/rabbit_tests.erl
+++ b/src/rabbit_tests.erl
@@ -50,7 +50,7 @@ all_tests() ->
passed = test_app_management(),
passed = test_log_management_during_startup(),
passed = test_statistics(),
- passed = test_option_parser(),
+ passed = test_arguments_parser(),
passed = test_cluster_management(),
passed = test_user_management(),
passed = test_runtime_parameters(),
@@ -801,27 +801,57 @@ test_log_management_during_startup() ->
ok = control_action(start_app, []),
passed.
-test_option_parser() ->
- %% command and arguments should just pass through
- ok = check_get_options({["mock_command", "arg1", "arg2"], []},
- [], ["mock_command", "arg1", "arg2"]),
+test_arguments_parser() ->
+ GlobalOpts1 = [{"-f1", flag}, {"-o1", {option, "foo"}}],
+ Commands1 = [command1, {command2, [{"-f2", flag}, {"-o2", {option, "bar"}}]}],
- %% get flags
- ok = check_get_options(
- {["mock_command", "arg1"], [{"-f", true}, {"-f2", false}]},
- [{flag, "-f"}, {flag, "-f2"}], ["mock_command", "arg1", "-f"]),
-
- %% get options
- ok = check_get_options(
- {["mock_command"], [{"-foo", "bar"}, {"-baz", "notbaz"}]},
- [{option, "-foo", "notfoo"}, {option, "-baz", "notbaz"}],
- ["mock_command", "-foo", "bar"]),
+ GetOptions =
+ fun (Args) ->
+ rabbit_misc:parse_arguments(Commands1, GlobalOpts1, Args)
+ end,
- %% shuffled and interleaved arguments and options
- ok = check_get_options(
- {["a1", "a2", "a3"], [{"-o1", "hello"}, {"-o2", "noto2"}, {"-f", true}]},
- [{option, "-o1", "noto1"}, {flag, "-f"}, {option, "-o2", "noto2"}],
- ["-f", "a1", "-o1", "hello", "a2", "a3"]),
+ check_parse_arguments(no_command, GetOptions, []),
+ check_parse_arguments(no_command, GetOptions, ["foo", "bar"]),
+ check_parse_arguments(
+ {ok, {command1, [{"-f1", false}, {"-o1", "foo"}], []}},
+ GetOptions, ["command1"]),
+ check_parse_arguments(
+ {ok, {command1, [{"-f1", false}, {"-o1", "blah"}], []}},
+ GetOptions, ["command1", "-o1", "blah"]),
+ check_parse_arguments(
+ {ok, {command1, [{"-f1", true}, {"-o1", "foo"}], []}},
+ GetOptions, ["command1", "-f1"]),
+ check_parse_arguments(
+ {ok, {command1, [{"-f1", false}, {"-o1", "blah"}], []}},
+ GetOptions, ["-o1", "blah", "command1"]),
+ check_parse_arguments(
+ {ok, {command1, [{"-f1", false}, {"-o1", "blah"}], ["quux"]}},
+ GetOptions, ["-o1", "blah", "command1", "quux"]),
+ check_parse_arguments(
+ {ok, {command1, [{"-f1", true}, {"-o1", "blah"}], ["quux", "baz"]}},
+ GetOptions, ["command1", "quux", "-f1", "-o1", "blah", "baz"]),
+ %% For duplicate flags, the last one counts
+ check_parse_arguments(
+ {ok, {command1, [{"-f1", false}, {"-o1", "second"}], []}},
+ GetOptions, ["-o1", "first", "command1", "-o1", "second"]),
+ %% If the flag "eats" the command, the command won't be recognised
+ check_parse_arguments(no_command, GetOptions,
+ ["-o1", "command1", "quux"]),
+ %% If a flag eats another flag, the eaten flag won't be recognised
+ check_parse_arguments(
+ {ok, {command1, [{"-f1", false}, {"-o1", "-f1"}], []}},
+ GetOptions, ["command1", "-o1", "-f1"]),
+
+ %% Now for some command-specific flags...
+ check_parse_arguments(
+ {ok, {command2, [{"-f1", false}, {"-f2", false},
+ {"-o1", "foo"}, {"-o2", "bar"}], []}},
+ GetOptions, ["command2"]),
+
+ check_parse_arguments(
+ {ok, {command2, [{"-f1", false}, {"-f2", true},
+ {"-o1", "baz"}, {"-o2", "bar"}], ["quux", "foo"]}},
+ GetOptions, ["-f2", "command2", "quux", "-o1", "baz", "foo"]),
passed.
@@ -1605,10 +1635,13 @@ expand_options(As, Bs) ->
end
end, Bs, As).
-check_get_options({ExpArgs, ExpOpts}, Defs, Args) ->
- {ExpArgs, ResOpts} = rabbit_misc:get_options(Defs, Args),
- true = lists:sort(ExpOpts) == lists:sort(ResOpts), % don't care about the order
- ok.
+check_parse_arguments(ExpRes, Fun, As) ->
+ SortRes =
+ fun (no_command) -> no_command;
+ ({ok, {C, KVs, As1}}) -> {ok, {C, lists:sort(KVs), As1}}
+ end,
+
+ true = SortRes(ExpRes) =:= SortRes(Fun(As)).
empty_files(Files) ->
[case file:read_file_info(File) of