diff options
-rw-r--r-- | src/rabbit_control.erl | 71 | ||||
-rw-r--r-- | src/rabbit_misc.erl | 102 | ||||
-rw-r--r-- | src/rabbit_plugins.erl | 30 | ||||
-rw-r--r-- | src/rabbit_tests.erl | 81 |
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 |