feat(bin/emqx): add cold_eval nodetool command

This commit is contained in:
Zaiming Shi 2021-09-04 23:05:41 +02:00
parent 6b7d3bcf98
commit 6f99f14540
2 changed files with 49 additions and 33 deletions

View File

@ -14,6 +14,11 @@ ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)"
# shellcheck disable=SC1090 # shellcheck disable=SC1090
. "$ROOT_DIR"/releases/emqx_vars . "$ROOT_DIR"/releases/emqx_vars
# defined in emqx_vars
export RUNNER_ROOT_DIR
export RUNNER_ETC_DIR
export REL_VSN
RUNNER_SCRIPT="$RUNNER_BIN_DIR/$REL_NAME" RUNNER_SCRIPT="$RUNNER_BIN_DIR/$REL_NAME"
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}" CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
REL_DIR="$RUNNER_ROOT_DIR/releases/$REL_VSN" REL_DIR="$RUNNER_ROOT_DIR/releases/$REL_VSN"
@ -109,7 +114,7 @@ relx_usage() {
echo " don't make it permanent" echo " don't make it permanent"
;; ;;
*) *)
echo "Usage: $REL_NAME {start|start_boot <file>|ertspath|foreground|stop|pid|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade|downgrade|install|uninstall|versions|escript|ctl|rpc|rpcterms|eval|root_dir}" echo "Usage: $REL_NAME {start|start_boot <file>|ertspath|foreground|stop|pid|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade|downgrade|install|uninstall|versions|escript|ctl|rpc|rpcterms|eval|cold_eval|root_dir}"
;; ;;
esac esac
} }
@ -198,18 +203,12 @@ relx_gen_id() {
# Control a node # Control a node
relx_nodetool() { relx_nodetool() {
command="$1"; shift command="$1"; shift
export RUNNER_ROOT_DIR
export REL_VSN
ERL_FLAGS="$ERL_FLAGS $EPMD_ARG" \ ERL_FLAGS="$ERL_FLAGS $EPMD_ARG" \
"$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \ "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \
-setcookie "$COOKIE" "$command" "$@" -setcookie "$COOKIE" "$command" "$@"
} }
call_hocon() { call_hocon() {
export RUNNER_ROOT_DIR
export RUNNER_ETC_DIR
export REL_VSN
"$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" hocon "$@" \ "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" hocon "$@" \
|| die "call_hocon_failed: $*" $? || die "call_hocon_failed: $*" $?
} }
@ -217,8 +216,6 @@ call_hocon() {
# Run an escript in the node's environment # Run an escript in the node's environment
relx_escript() { relx_escript() {
shift; scriptpath="$1"; shift shift; scriptpath="$1"; shift
export RUNNER_ROOT_DIR
"$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" "$@" "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" "$@"
} }
@ -709,6 +706,10 @@ case "$1" in
shift shift
relx_nodetool "eval" "$@" relx_nodetool "eval" "$@"
;; ;;
cold_eval)
shift;
"$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" cold_eval "$@"
;;
*) *)
relx_usage "$1" relx_usage "$1"
exit 1 exit 1

View File

@ -24,6 +24,8 @@ main(Args) ->
["hocon" | Rest] -> ["hocon" | Rest] ->
%% forward the call to hocon_cli %% forward the call to hocon_cli
hocon_cli:main(Rest); hocon_cli:main(Rest);
["cold_eval" | Rest] ->
code_eval(Rest);
_ -> _ ->
do(Args) do(Args)
end. end.
@ -117,40 +119,53 @@ do(Args) ->
io:format("~p\n", [Other]) io:format("~p\n", [Other])
end; end;
["eval" | ListOfArgs] -> ["eval" | ListOfArgs] ->
% shells may process args into more than one, and end up stripping Parsed = parse_eval_args(ListOfArgs),
% spaces, so this converts all of that to a single string to parse
String = binary_to_list(
list_to_binary(
join(ListOfArgs," ")
)
),
% then just as a convenience to users, if they forgot a trailing
% '.' add it for them.
Normalized =
case lists:reverse(String) of
[$. | _] -> String;
R -> lists:reverse([$. | R])
end,
% then scan and parse the string
{ok, Scanned, _} = erl_scan:string(Normalized),
{ok, Parsed } = erl_parse:parse_exprs(Scanned),
% and evaluate it on the remote node % and evaluate it on the remote node
case rpc:call(TargetNode, erl_eval, exprs, [Parsed, [] ]) of case rpc:call(TargetNode, erl_eval, exprs, [Parsed, [] ]) of
{value, Value, _} -> {value, Value, _} ->
io:format ("~p\n",[Value]); io:format ("~p~n",[Value]);
{badrpc, Reason} -> {badrpc, Reason} ->
io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), io:format("RPC to ~p failed: ~p~n", [TargetNode, Reason]),
halt(1) halt(1)
end; end;
Other -> Other ->
io:format("Other: ~p\n", [Other]), io:format("Other: ~p~n", [Other]),
io:format("Usage: nodetool {genconfig, chkconfig|getpid|ping|stop|rpc|rpc_infinity|rpcterms|eval [Terms]} [RPC]\n") io:format("Usage: nodetool chkconfig|getpid|ping|stop|rpc|rpc_infinity|rpcterms|eval|cold_eval [Terms] [RPC]\n")
end, end,
net_kernel:stop(). net_kernel:stop().
code_eval(Args) ->
Parsed = parse_eval_args(Args),
case erl_eval:exprs(Parsed, []) of
{value, Value, _} ->
io:format ("~p~n", [Value]);
Other ->
io:format("cold_eval_failed_with: ~p~n", [Other]),
halt(1)
end.
parse_eval_args(Args) ->
% shells may process args into more than one, and end up stripping
% spaces, so this converts all of that to a single string to parse
String = binary_to_list(
list_to_binary(
join(Args," ")
)
),
% then just as a convenience to users, if they forgot a trailing
% '.' add it for them.
Normalized =
case lists:reverse(String) of
[$. | _] -> String;
R -> lists:reverse([$. | R])
end,
% then scan and parse the string
{ok, Scanned, _} = erl_scan:string(Normalized),
{ok, Parsed } = erl_parse:parse_exprs(Scanned),
Parsed.
do_with_ret(Args, Name, Handler) -> do_with_ret(Args, Name, Handler) ->
{arity, Arity} = erlang:fun_info(Handler, arity), {arity, Arity} = erlang:fun_info(Handler, arity),
case take_args(Args, Name, Arity) of case take_args(Args, Name, Arity) of