From 09838d50c66490906da81257abc48a0ff3c927cd Mon Sep 17 00:00:00 2001 From: Luis Rascao Date: Sun, 7 Feb 2016 02:49:14 +0000 Subject: [PATCH] Add support for new relx directive that provides start/stop script hooks New 'extended_start_script_hooks' directive that allows the developer to define four different hook scripts to be invoked at pre/post start/stop phases. The hook scripts are invoked with the 'source' command, therefore they have access to all the variables in the start script. --- priv/templates/extended_bin | 27 +++++++++++++++---- src/rlx_prv_assembler.erl | 15 ++++++++--- test/rlx_release_SUITE.erl | 54 ++++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 9 deletions(-) diff --git a/priv/templates/extended_bin b/priv/templates/extended_bin index 7a9f0c7b5..e19aa68cf 100755 --- a/priv/templates/extended_bin +++ b/priv/templates/extended_bin @@ -16,6 +16,12 @@ REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN" ERL_OPTS="{{ erl_opts }}" RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}" +# start/stop pre/post hooks +PRE_START_HOOK="{{ pre_start_hook }}" +POST_START_HOOK="{{ post_start_hook }}" +PRE_STOP_HOOK="{{ pre_stop_hook }}" +POST_STOP_HOOK="{{ post_stop_hook }}" + find_erts_dir() { __erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN" if [ -d "$__erts_dir" ]; then @@ -234,11 +240,15 @@ case "$1" in mkdir -p "$PIPE_DIR" + [ "$PRE_START_HOOK" ] && . "$PRE_START_HOOK" "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \ - "$(relx_start_command)" + "$(relx_start_command)" & + [ "$POST_START_HOOK" ] && . "$POST_START_HOOK" + wait $! ;; stop) + [ "$PRE_STOP_HOOK" ] && . "$PRE_STOP_HOOK" # Wait for the node to completely stop... PID="$(relx_get_pid)" if ! relx_nodetool "stop"; then @@ -248,6 +258,7 @@ case "$1" in do sleep 1 done + [ "$POST_STOP_HOOK" ] && . "$POST_STOP_HOOK" ;; restart) @@ -389,8 +400,11 @@ case "$1" in echo "$RELEASE_ROOT_DIR" logger -t "$REL_NAME[$$]" "Starting up" - # Start the VM - exec "$@" -- ${1+$ARGS} + # Start the VM, invoke pre/post hooks + [ "$PRE_START_HOOK" ] && . "$PRE_START_HOOK" + exec "$@" -- ${1+$ARGS} & + [ "$POST_START_HOOK" ] && . "$POST_START_HOOK" + wait $! ;; foreground) @@ -421,8 +435,11 @@ case "$1" in echo "Exec: $@" -- ${1+$ARGS} echo "Root: $ROOTDIR" - # Start the VM - exec "$@" -- ${1+$ARGS} + # Start the VM, invoke pre/post hooks + [ "$PRE_START_HOOK" ] && . "$PRE_START_HOOK" + exec "$@" -- ${1+$ARGS} & + [ "$POST_START_HOOK" ] && . "$POST_START_HOOK" + wait $! ;; rpc) # Make sure a node IS running diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl index 2f0800583..dd886e450 100644 --- a/src/rlx_prv_assembler.erl +++ b/src/rlx_prv_assembler.erl @@ -355,7 +355,10 @@ write_bin_file(State, Release, OutputDir, RelDir) -> false -> ok end, - extended_bin_file_contents(OsFamily, RelName, RelVsn, rlx_release:erts(Release), ErlOpts) + Hooks = rlx_state:get(State, extended_start_script_hooks, []), + extended_bin_file_contents(OsFamily, RelName, RelVsn, + rlx_release:erts(Release), ErlOpts, + Hooks) end, %% We generate the start script by default, unless the user %% tells us not too @@ -613,13 +616,19 @@ bin_file_contents(OsFamily, RelName, RelVsn, ErtsVsn, ErlOpts) -> render(Template, [{rel_name, RelName}, {rel_vsn, RelVsn}, {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}]). -extended_bin_file_contents(OsFamily, RelName, RelVsn, ErtsVsn, ErlOpts) -> +extended_bin_file_contents(OsFamily, RelName, RelVsn, ErtsVsn, ErlOpts, Hooks) -> Template = case OsFamily of unix -> extended_bin; win32 -> extended_bin_windows end, + PreStartHook = proplists:get_value(pre_start, Hooks, ""), + PostStartHook = proplists:get_value(post_start, Hooks, ""), + PreStopHook = proplists:get_value(pre_stop, Hooks, ""), + PostStopHook = proplists:get_value(post_stop, Hooks, ""), render(Template, [{rel_name, RelName}, {rel_vsn, RelVsn}, - {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}]). + {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}, + {pre_start_hook, PreStartHook}, {post_start_hook, PostStartHook}, + {pre_stop_hook, PreStopHook}, {post_stop_hook, PostStopHook}]). erl_ini(OutputDir, ErtsVsn) -> ErtsDirName = string:concat("erts-", ErtsVsn), diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl index 780b1c838..38147e414 100644 --- a/test/rlx_release_SUITE.erl +++ b/test/rlx_release_SUITE.erl @@ -45,7 +45,8 @@ make_dev_mode_release/1, make_config_script_release/1, make_release_twice/1, - make_release_twice_dev_mode/1]). + make_release_twice_dev_mode/1, + make_release_start_script_hooks/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -1036,6 +1037,57 @@ make_release_twice_dev_mode(Config) -> ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs1)), ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs1)). +make_release_start_script_hooks(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app", "0.0.1", [stdlib,kernel], []), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {generate_start_script, true}, + {extended_start_script, true}, + {extended_start_script_hooks, [ + {pre_start, "./scripts/pre_start"}, + {post_start, "./scripts/post_start"}, + {pre_stop, "./scripts/pre_stop"}, + {post_stop, "./scripts/post_stop"} + ]}, + {mkdir, "scripts"}, + {overlay, [{copy, "./pre_start", "scripts/pre_start"}, + {copy, "./post_start", "scripts/post_start"}, + {copy, "./pre_stop", "scripts/pre_stop"}, + {copy, "./post_stop", "scripts/post_stop"}]} + ]), + + %% write the hook scripts, each of them will write an erlang term to a file + %% that will later be consulted + ok = file:write_file(filename:join([LibDir1, "./pre_start"]), + "#!/bin/bash\n# $*\necho \\{pre_start, $REL_NAME, \\'$NAME\\', $COOKIE\\}. >> test"), + ok = file:write_file(filename:join([LibDir1, "./post_start"]), + "#!/bin/bash\n# $*\necho \\{post_start, $REL_NAME, \\'$NAME\\', $COOKIE\\}. >> test"), + ok = file:write_file(filename:join([LibDir1, "./pre_stop"]), + "#!/bin/bash\n# $*\necho \\{pre_stop, $REL_NAME, \\'$NAME\\', $COOKIE\\}. >> test"), + ok = file:write_file(filename:join([LibDir1, "./post_stop"]), + "#!/bin/bash\n# $*\necho \\{post_stop, $REL_NAME, \\'$NAME\\', $COOKIE\\}. >> test"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, _State} = relx:do(foo, undefined, [], [LibDir1], 3, + OutputDir, ConfigFile), + %% now start/stop the release to make sure the script hooks are really getting + %% executed + os:cmd(filename:join([OutputDir, "foo", "bin", "foo start"])), + timer:sleep(2000), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo stop"])), + %% now check that the output file contains the expected format + {ok,[{pre_start, foo, _, foo}, + {post_start, foo, _, foo}, + {pre_stop, foo, _, foo}, + {post_stop, foo, _, foo}]} = file:consult(filename:join([OutputDir, "foo", "test"])). + %%%=================================================================== %%% Helper Functions %%%===================================================================