Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No longer require hooks/extensions must be added to overlays #865

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions priv/templates/extended_bin
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ PRE_INSTALL_UPGRADE_HOOKS="{{{ pre_install_upgrade_hooks }}}"
POST_INSTALL_UPGRADE_HOOKS="{{{ post_install_upgrade_hooks }}}"
STATUS_HOOK="{{{ status_hook }}}"
EXTENSIONS="{{{ extensions }}}"
EXTENSION_DESCRIPTIONS="{{{ extension_descriptions }}}"

relx_usage() {
command="$1"
Expand Down Expand Up @@ -188,7 +189,7 @@ Commands:
versions Print versions of the release available
escript Run an escript in the same environment as the release
status Verify node is running and then run status hook scripts
$EXTENSIONS"
$(relx_extension_descriptions)"
fi
;;
esac
Expand Down Expand Up @@ -496,9 +497,9 @@ relx_run_hooks() {
set $(echo "$hook" | sed -e 's/|/ /g')
HOOK_SCRIPT=$1; shift
# all hook locations are expected to be
# relative to the start script location
# relative to the release root
# shellcheck disable=SC1090,SC2240
[ -f "$SCRIPT_DIR/$HOOK_SCRIPT" ] && . "$SCRIPT_DIR/$HOOK_SCRIPT" "$@"
[ -f "$RELEASE_ROOT_DIR/$HOOK_SCRIPT" ] && . "$RELEASE_ROOT_DIR/$HOOK_SCRIPT" "$@"
done
}

Expand Down Expand Up @@ -542,9 +543,23 @@ relx_run_extension() {
EXTENSION_SCRIPT=$1
shift
# all extension script locations are expected to be
# relative to the start script location
# relative to the release root
# shellcheck disable=SC1090,SC2240
[ -f "$SCRIPT_DIR/$EXTENSION_SCRIPT" ] && . "$SCRIPT_DIR/$EXTENSION_SCRIPT" "$@"
[ -f "$RELEASE_ROOT_DIR/$EXTENSION_SCRIPT" ] && . "$RELEASE_ROOT_DIR/$EXTENSION_SCRIPT" "$@"
}

relx_extension_descriptions() {
IFS0=$IFS
IFS='|'
for description in $EXTENSION_DESCRIPTIONS
do
cmd=$(echo "$description" |cut -d '=' -f1)
descr=$(echo "$description" |cut -d '=' -f2)
# 23 is the magic number of spaces that take up
# the space between a command and it's description
printf ' %-23s %s\n' "$cmd" "$descr"
done
IFS=$IFS0
}

# given a list of arguments, identify the internal ones
Expand Down Expand Up @@ -979,7 +994,7 @@ case "$1" in
ping_or_exit

# shellcheck disable=SC1090,SC2240
[ -n "${STATUS_HOOK}" ] && [ -f "$SCRIPT_DIR/$STATUS_HOOK" ] && . "$SCRIPT_DIR/$STATUS_HOOK" "$@"
[ -n "${STATUS_HOOK}" ] && [ -f "$RELEASE_ROOT_DIR/$STATUS_HOOK" ] && . "$RELEASE_ROOT_DIR/$STATUS_HOOK" "$@"
;;
help)
if [ -z "$2" ]; then
Expand Down
14 changes: 8 additions & 6 deletions shelltests/extension_tests/rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

{relx, [{release, {extension_tests, "0.1.0"},
[extension_tests]},
{extended_start_script_extensions, [{bar, "extensions/bar"},
{foo, "extensions/foo"},
{baz, "extensions/baz"}]},
{overlay, [{copy, "./bar", "bin/extensions/bar"},
{copy, "./foo", "bin/extensions/foo"},
{copy, "./baz", "bin/extensions/baz"}]}
{extended_start_script_extensions, [
% auto copied over to `bin/extensions/bar` with empty description
{bar, "./bar"},
% auto copied over to `bin/extensions/foo` with description `"foo description"`
{foo, "./foo", "foo description"},
% auto copied over to `bin/extensions/baz` with description `"baz description"`
{baz, "./baz", "bin/extensions/baz", "baz description"}
]}
]}.
3 changes: 3 additions & 0 deletions shelltests/hooks_tests/hooks/post_start2
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
# $*
echo \{post_start2, $REL_NAME, \'$NAME\', $COOKIE\}. >> test
1 change: 1 addition & 0 deletions shelltests/hooks_tests/hooks_tests.test
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ $ cat ./_build/default/rel/hooks_tests/test
>
{pre_start, hooks_tests, 'hooks_tests@localhost', hooks_tests}.
{post_start, hooks_tests, 'hooks_tests@localhost', hooks_tests}.
{post_start2, hooks_tests, 'hooks_tests@localhost', hooks_tests}.
{pre_stop, hooks_tests, 'hooks_tests@localhost', hooks_tests}.
{post_stop, hooks_tests, 'hooks_tests@localhost', hooks_tests}.
>= 0
Expand Down
43 changes: 21 additions & 22 deletions shelltests/hooks_tests/rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,24 @@
[hooks_tests]},
{vm_args, "config/vm.args"},
{extended_start_script_hooks, [
{pre_start, [
{custom, "hooks/pre_start"}
]},
{post_start, [
wait_for_vm_start,
{pid, "foo.pid"},
wait_for_vm_start,
{custom, "hooks/post_start"}
]},
{pre_stop, [
{custom, "hooks/pre_stop"}
]},
{post_stop, [
{custom, "hooks/post_stop"}
]},
{status, [
{custom, "hooks/status"}
]}
]},

{overlay, [{copy, "./hooks/status", "bin/hooks/status"},
{copy, "./hooks/{pre,post}_{start,stop}", "bin/hooks/"}]}]}.
{pre_start, [
{custom, "hooks/pre_start"}
]},
{post_start, [
wait_for_vm_start,
{pid, "foo.pid"},
wait_for_vm_start,
{custom, "hooks/post_start"},
{custom, "hooks/post_start2", "bin/hooks/extra/post_start"}
]},
{pre_stop, [
{custom, "hooks/pre_stop"}
]},
{post_stop, [
{custom, "hooks/post_stop"}
]},
{status, [
{custom, "hooks/status"}
]}
]}
]}.
132 changes: 89 additions & 43 deletions src/rlx_assemble.erl
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,25 @@ create_release(State0, Release0, OutputDir) ->
ok = rlx_file_utils:write_term(StartCleanFile, StartCleanMeta),
ok = rlx_file_utils:write_term(NoDotErlFile, NoDotErlMeta),
write_bin_file(State1, Release1, OutputDir, ReleaseDir),
{ok, State1}.
% check for existing start script extensions and append
% the overlays necessary for them to be copied over to the
% release
State2 = apply_extension_overlays(
rlx_state:extended_start_script_extensions(State1), State1),
{ok, State2}.

apply_extension_overlays([], State) -> State;
apply_extension_overlays([{Name, ExtensionSrc} | Rest], State) ->
ExtensionTarget = extension_default_target(ExtensionSrc),
apply_extension_overlays([{Name, ExtensionSrc, ExtensionTarget} | Rest], State);
apply_extension_overlays([{Name, ExtensionSrc, _ExtensionDescription} | Rest], State) ->
ExtensionTarget = extension_default_target(ExtensionSrc),
apply_extension_overlays([{Name, ExtensionSrc, ExtensionTarget, _ExtensionDescription} | Rest], State);
apply_extension_overlays([{_, ExtensionSrc, ExtensionTarget, _} | Rest], State0) ->
State1 = rlx_state:overlay(State0,
[{copy, ExtensionSrc, ExtensionTarget} | rlx_state:overlay(State0)]),
% create the overlay instruction that will copy the extension script to it's release location
apply_extension_overlays(Rest, State1).

write_bin_file(State, Release, OutputDir, RelDir) ->
BinDir = filename:join([OutputDir, "bin"]),
Expand Down Expand Up @@ -317,7 +335,7 @@ write_start_scripts_for(Type, Release, OutputDir, State) ->
%% extended start script needs nodetool so it's
%% always included
include_nodetool(BinDir),
Hooks = expand_hooks(BinDir,
Hooks = expand_hooks(OutputDir,
rlx_state:extended_start_script_hooks(State),
State),
Extensions = rlx_state:extended_start_script_extensions(State),
Expand Down Expand Up @@ -361,20 +379,20 @@ write_start_script(BaseName, Type, StartFile) ->
ok = file:write_file(RelStartFile, StartFile),
ok = file:change_mode(RelStartFile, 8#755).

expand_hooks(_Bindir, [], _State) -> [];
expand_hooks(BinDir, Hooks, _State) ->
expand_hooks(BinDir, Hooks, [], _State).
expand_hooks(_OutputDir, [], _State) -> [];
expand_hooks(OutputDir, Hooks, _State) ->
expand_hooks(OutputDir, Hooks, [], _State).

expand_hooks(_BinDir, [], Acc, _State) -> Acc;
expand_hooks(BinDir, [{Phase, Hooks0} | Rest], Acc, State) ->
expand_hooks(_OutputDir, [], Acc, _State) -> Acc;
expand_hooks(OutputDir, [{Phase, Hooks0} | Rest], Acc, State) ->
%% filter and expand hooks to their respective shell scripts
Hooks =
lists:foldl(
fun(Hook, Acc0) ->
case validate_hook(Phase, Hook) of
true ->
%% all hooks are relative to the bin dir
HookScriptFilename = filename:join([BinDir,
%% all hooks are relative to the release root dir
HookScriptFilename = filename:join([OutputDir,
hook_filename(Hook)]),
%% write the hook script file to it's proper location
ok = render_hook(hook_template(Hook), HookScriptFilename, State),
Expand All @@ -386,7 +404,7 @@ expand_hooks(BinDir, [{Phase, Hooks0} | Rest], Acc, State) ->
Acc0
end
end, [], Hooks0),
expand_hooks(BinDir, Rest, Acc ++ [{Phase, Hooks}], State).
expand_hooks(OutputDir, Rest, Acc ++ [{Phase, Hooks}], State).

%% the pid script hook is only allowed in the
%% post_start phase
Expand All @@ -399,43 +417,48 @@ validate_hook(post_start, wait_for_vm_start) -> true;
validate_hook(post_start, {wait_for_process, _}) -> true;
%% custom hooks are allowed in all phases
validate_hook(_Phase, {custom, _}) -> true;
validate_hook(_Phase, {custom, _, _}) -> true;
%% as well as status hooks
validate_hook(status, _) -> true;
%% deny all others
validate_hook(_, _) -> false.

hook_filename({custom, CustomScript}) -> CustomScript;
hook_filename(pid) -> "hooks/builtin/pid";
hook_filename({pid, _}) -> "hooks/builtin/pid";
hook_filename(wait_for_vm_start) -> "hooks/builtin/wait_for_vm_start";
hook_filename({wait_for_process, _}) -> "hooks/builtin/wait_for_process";
hook_filename(builtin_status) -> "hooks/builtin/status".

hook_invocation({custom, CustomScript}) -> CustomScript;
% custom hook target location defaults to `bin/hooks`
hook_filename({custom, Src}) ->
filename:join(["bin/hooks", filename:basename(Src)]);
hook_filename({custom, _, Target}) -> Target;
hook_filename(pid) -> "bin/hooks/builtin/pid";
hook_filename({pid, _}) -> "bin/hooks/builtin/pid";
hook_filename(wait_for_vm_start) -> "bin/hooks/builtin/wait_for_vm_start";
hook_filename({wait_for_process, _}) -> "bin/hooks/builtin/wait_for_process";
hook_filename(builtin_status) -> "bin/hooks/builtin/status".

hook_invocation({custom, Src}) ->
filename:join(["bin/hooks", filename:basename(Src)]);
hook_invocation({custom, _, Target}) ->
Target;
%% the pid builtin hook with no arguments writes to pid file
%% at /var/run/{{ rel_name }}.pid
hook_invocation(pid) -> rlx_string:join(["hooks/builtin/pid",
hook_invocation(pid) -> rlx_string:join(["bin/hooks/builtin/pid",
"/var/run/$REL_NAME.pid"], "|");
hook_invocation({pid, PidFile}) -> rlx_string:join(["hooks/builtin/pid",
hook_invocation({pid, PidFile}) -> rlx_string:join(["bin/hooks/builtin/pid",
PidFile], "|");
hook_invocation(wait_for_vm_start) -> "hooks/builtin/wait_for_vm_start";
hook_invocation(wait_for_vm_start) -> "bin/hooks/builtin/wait_for_vm_start";
hook_invocation({wait_for_process, Name}) ->
%% wait_for_process takes an atom as argument
%% which is the process name to wait for
rlx_string:join(["hooks/builtin/wait_for_process",
rlx_string:join(["bin/hooks/builtin/wait_for_process",
atom_to_list(Name)], "|");
hook_invocation(builtin_status) -> "hooks/builtin/status".
hook_invocation(builtin_status) -> "bin/hooks/builtin/status".

hook_template({custom, _}) -> custom;
hook_template({custom, Src}) -> {file, Src};
hook_template({custom, Src, _}) -> {file, Src};
hook_template(pid) -> builtin_hook_pid;
hook_template({pid, _}) -> builtin_hook_pid;
hook_template(wait_for_vm_start) -> builtin_hook_wait_for_vm_start;
hook_template({wait_for_process, _}) -> builtin_hook_wait_for_process;
hook_template(builtin_status) -> builtin_hook_status.

%% custom hooks are not rendered, they should
%% be copied by the release overlays
render_hook(custom, _, _) -> ok;
render_hook(TemplateName, Script, _State) ->
?log_info("rendering ~p hook to ~s", [TemplateName, rlx_file_utils:print_path(Script)]),
Template = render(TemplateName),
Expand Down Expand Up @@ -966,6 +989,38 @@ bin_file_contents(Type, RelName, RelVsn, ErtsVsn) ->
render(Template, [{rel_name, RelName}, {rel_vsn, RelVsn},
{erts_vsn, ErtsVsn}]).

extension_default_target(Src) ->
filename:join("bin/extensions", filename:basename(Src)).

extension_default_description(_) ->
"".

extensions(Extensions) ->
extensions(Extensions, {[], [], []}).

extensions([], {ExtensionsList0, ExtensionDeclarations0, ExtensionDescriptions0}) ->
% pipe separated string of extensions
% (eg. foo|bar|baz|undefined)
ExtensionsList = rlx_string:join(ExtensionsList0 ++ ["undefined"], "|"),
% command separated string of extension script declarations
% (eg. foo_extension="path/to/foo_script:bar_extension="path/to/bar_script")
ExtensionDeclarations = rlx_string:join(ExtensionDeclarations0, ";"),
% (eg. baz=baz description|foo=foo description|bar=)
ExtensionDescriptions = rlx_string:join(ExtensionDescriptions0, "|"),
{ExtensionsList, ExtensionDeclarations, ExtensionDescriptions};
extensions([{Name, Src} | Rest], Acc) ->
extensions([{Name, Src, extension_default_target(Src), extension_default_description(Name)} | Rest], Acc);
extensions([{Name, Src, Description} | Rest], Acc) ->
extensions([{Name, Src, extension_default_target(Src), Description} | Rest], Acc);
extensions([{Name, _Src, Target, Description} | Rest],
{Acc0, Acc1, Acc2}) ->
NameStr = atom_to_list(Name),
% eg. bar_extension=bin/extensions/bar
ExtensionDeclaration = NameStr ++ "_extension=\"" ++ Target ++ "\"",
% eg. bar=bar description
ExtensionDescription = NameStr ++ "=" ++ Description,
extensions(Rest, {[atom_to_list(Name) | Acc0], [ExtensionDeclaration | Acc1], [ExtensionDescription | Acc2]}).

extended_bin_file_contents(Type, RelName, RelVsn, ErtsVsn, Hooks, Extensions) ->
Template = case Type of
unix -> extended_bin;
Expand All @@ -982,21 +1037,7 @@ extended_bin_file_contents(Type, RelName, RelVsn, ErtsVsn, Hooks, Extensions) ->
PostInstallUpgradeHooks = rlx_string:join(proplists:get_value(post_install_upgrade,
Hooks, []), " "),
StatusHook = rlx_string:join(proplists:get_value(status, Hooks, []), " "),
{ExtensionsList1, ExtensionDeclarations1} =
lists:foldl(fun({Name, Script},
{ExtensionsList0, ExtensionDeclarations0}) ->
ExtensionDeclaration = atom_to_list(Name) ++
"_extension=\"" ++
Script ++ "\"",
{ExtensionsList0 ++ [atom_to_list(Name)],
ExtensionDeclarations0 ++ [ExtensionDeclaration]}
end, {[], []}, Extensions),
% pipe separated string of extensions, to show on the start script usage
% (eg. foo|bar)
ExtensionsList = rlx_string:join(ExtensionsList1 ++ ["undefined"], "|"),
% command separated string of extension script declarations
% (eg. foo_extension="path/to/foo_script")
ExtensionDeclarations = rlx_string:join(ExtensionDeclarations1, ";"),
{ExtensionsList, ExtensionDeclarations, ExtensionDescriptions} = extensions(Extensions),
render(Template, [{rel_name, RelName}, {rel_vsn, RelVsn},
{erts_vsn, ErtsVsn},
{pre_start_hooks, PreStartHooks},
Expand All @@ -1007,7 +1048,8 @@ extended_bin_file_contents(Type, RelName, RelVsn, ErtsVsn, Hooks, Extensions) ->
{post_install_upgrade_hooks, PostInstallUpgradeHooks},
{status_hook, StatusHook},
{extensions, ExtensionsList},
{extension_declarations, ExtensionDeclarations}]).
{extension_declarations, ExtensionDeclarations},
{extension_descriptions, ExtensionDescriptions}]).

install_upgrade_escript_contents() ->
render(install_upgrade_escript).
Expand All @@ -1027,6 +1069,10 @@ vm_args_file(RelName) ->
render(Template) ->
render(Template, []).

render({file, Name}, Data) ->
{ok, Tpl} = file:read_file(Name),
{ok, Content} = rlx_util:render(Tpl, Data),
Content;
render(Template, Data) ->
Files = rlx_util:template_files(),
Tpl = rlx_util:load_file(Files, escript, atom_to_list(Template)),
Expand Down