diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/rlx_archive_SUITE.erl | 33 | ||||
-rw-r--r-- | test/rlx_eunit_SUITE.erl | 8 | ||||
-rw-r--r-- | test/rlx_extended_bin_SUITE.erl | 1240 | ||||
-rw-r--r-- | test/rlx_release_SUITE.erl | 205 | ||||
-rw-r--r-- | test/rlx_test_utils.erl | 133 |
5 files changed, 1588 insertions, 31 deletions
diff --git a/test/rlx_archive_SUITE.erl b/test/rlx_archive_SUITE.erl index 08da2b8..5122c11 100644 --- a/test/rlx_archive_SUITE.erl +++ b/test/rlx_archive_SUITE.erl @@ -249,6 +249,8 @@ overlay_archive(Config) -> TestDirFull = filename:join([LibDir1, TestDir]), TestFileFull = filename:join(TestDirFull, TestFile), SecondTestDir = "second_test_dir", + TestScript = "test_script", + TestScript2 = "test_script2", rlx_test_utils:write_config(ConfigFile, [{overlay_vars, [OverlayVars1, OverlayVars2]}, {overlay, [{mkdir, "{{target_dir}}/fooo"}, @@ -260,9 +262,17 @@ overlay_archive(Config) -> "{{target_dir}}/{{yahoo}}/vars.link.config"}, {copy, TestDirFull, "{{target_dir}}/"++SecondTestDir++"/"}, + {copy, TestScript, + "{{target_dir}}/"++SecondTestDir++"/"++TestScript}, + {chmod, 8#00700, + "{{target_dir}}/"++SecondTestDir++"/"++TestScript}, + {copy, TestScript2, + "{{target_dir}}/"++SecondTestDir++"/"++TestScript2}, + {chmod, "{{test_script_perm}}", + "{{target_dir}}/"++SecondTestDir++"/"++TestScript2}, {template, Template, "{{target_dir}}/test_template_resolved"}, - {template, Template, + {template, Template, "bin/{{default_release_name}}-{{default_release_version}}"}]}, {release, {foo, "0.0.1"}, [goal_app_1, @@ -272,7 +282,8 @@ overlay_archive(Config) -> rlx_test_utils:write_config(VarsFile1, [{yahoo, "yahoo"}, {yahoo2, [{foo, "bar"}]}, {foo_yahoo, "foo_{{yahoo}}"}, - {foo_dir, "foodir"}]), + {foo_dir, "foodir"}, + {test_script_perm,8#00770}]), VarsFile2 = filename:join([LibDir1, "vars2.config"]), rlx_test_utils:write_config(VarsFile2, [{google, "yahoo"}, @@ -283,6 +294,11 @@ overlay_archive(Config) -> rlx_test_utils:write_config(VarsFile3, [{google, "yahoo"}, {yahoo4, "{{yahoo}}/{{yahoo2}}4"}]), + TestScriptFile = filename:join([LibDir1,TestScript]), + ok = file:write_file(TestScriptFile, <<"#!/bin/sh\necho \"hello world\"">>), + TestScriptFile2 = filename:join([LibDir1,TestScript2]), + ok = file:write_file(TestScriptFile2, <<"#!/bin/sh\necho \"hello world 2\"">>), + ok = rlx_util:mkdir_p(TestDirFull), ok = file:write_file(TestFileFull, rlx_test_utils:test_template_contents()), @@ -312,6 +328,19 @@ overlay_archive(Config) -> ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)), + % check that the chmod of our file worked + ChmodedFile = filename:join([OutputDir,"foo",SecondTestDir,TestScript]), + {ok, ChmodedInfo} = file:read_file_info (ChmodedFile), + % mode from file_info is a bitmask which might have other bits set, but + % if we mask those we care about and check we should get true, see details + % here http://stackoverflow.com/questions/13183838/how-to-use-erlang-fileread-file-info-permissions-mode-info + ?assert(ChmodedInfo#file_info.mode band 8#00700 =:= 8#00700), + + % check that the templated chmod of our file worked + ChmodedFile2 = filename:join([OutputDir,"foo",SecondTestDir,TestScript2]), + {ok, ChmodedInfo2} = file:read_file_info (ChmodedFile2), + ?assert(ChmodedInfo2#file_info.mode band 8#00770 =:= 8#00770), + TarFile = filename:join([OutputDir, "foo", "foo-0.0.1.tar.gz"]), {ok, Files} = erl_tar:table(TarFile, [compressed]), ?assert(lists:any(fun(X) -> re:run(X, "lib/stdlib-.*/src/.*") =/= nomatch end, Files)), diff --git a/test/rlx_eunit_SUITE.erl b/test/rlx_eunit_SUITE.erl index d429f36..874e5a6 100644 --- a/test/rlx_eunit_SUITE.erl +++ b/test/rlx_eunit_SUITE.erl @@ -23,7 +23,8 @@ all/0, depsolver/1, goal/1, - app_info/1]). + app_info/1, + topo/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -38,7 +39,7 @@ end_per_suite(_Config) -> ok. all() -> - [depsolver, goal, app_info]. + [depsolver, goal, app_info, topo]. depsolver(_Config) -> ok = eunit:test(rlx_depsolver). @@ -48,3 +49,6 @@ goal(_Config) -> app_info(_Config) -> ok = eunit:test(rlx_app_info). + +topo(_Config) -> + ok = eunit:test(rlx_topo). diff --git a/test/rlx_extended_bin_SUITE.erl b/test/rlx_extended_bin_SUITE.erl index ce72437..c2e6bc2 100644 --- a/test/rlx_extended_bin_SUITE.erl +++ b/test/rlx_extended_bin_SUITE.erl @@ -22,13 +22,34 @@ end_per_suite/1, init_per_testcase/2, all/0, + start_sname_in_other_argsfile/1, + start_fail_when_no_name/1, + start_fail_when_multiple_names/1, + start_fail_when_missing_argsfile/1, + start_fail_when_nonreadable_argsfile/1, + start_fail_when_relative_argsfile/1, + start_fail_when_circular_argsfiles/1, ping/1, + shortname_ping/1, + longname_ping/1, attach/1, pid/1, restart/1, reboot/1, escript/1, - remote_console/1]). + remote_console/1, + replace_os_vars/1, + replace_os_vars_multi_node/1, + replace_os_vars_included_config/1, + replace_os_vars_custom_location/1, + replace_os_vars_dev_mode/1, + replace_os_vars_twice/1, + custom_start_script_hooks/1, + builtin_wait_for_vm_start_script_hook/1, + builtin_pid_start_script_hook/1, + builtin_wait_for_process_start_script_hook/1, + mixed_custom_and_builtin_start_script_hooks/1, + builtin_status_script/1, custom_status_script/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -53,8 +74,15 @@ init_per_testcase(_, Config) -> {state, State1} | Config]. all() -> - [ping, attach, pid, restart, reboot, escript, - remote_console]. + [start_sname_in_other_argsfile, start_fail_when_no_name, start_fail_when_multiple_names, + start_fail_when_missing_argsfile, start_fail_when_nonreadable_argsfile, + start_fail_when_relative_argsfile, start_fail_when_circular_argsfiles, + ping, shortname_ping, longname_ping, attach, pid, restart, reboot, escript, + remote_console, replace_os_vars, replace_os_vars_multi_node, replace_os_vars_included_config, + replace_os_vars_custom_location, replace_os_vars_dev_mode, replace_os_vars_twice, custom_start_script_hooks, + builtin_wait_for_vm_start_script_hook, builtin_pid_start_script_hook, + builtin_wait_for_process_start_script_hook, mixed_custom_and_builtin_start_script_hooks, + builtin_status_script, custom_status_script]. ping(Config) -> LibDir1 = proplists:get_value(lib1, Config), @@ -87,7 +115,85 @@ ping(Config) -> {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), %% a ping should fail after stopping a node - {error, 1} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). + +shortname_ping(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"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + ec_file:write(VmArgs, "-sname foo\n\n" + "-setcookie cookie\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + %% now start/stop the release to make sure the extended script is working + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"])), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), + %% a ping should fail after stopping a node + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). + +longname_ping(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"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + ec_file:write(VmArgs, "-name foo\n\n" + "-setcookie cookie\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + %% now start/stop the release to make sure the extended script is working + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"])), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), + %% a ping should fail after stopping a node + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). attach(Config) -> LibDir1 = proplists:get_value(lib1, Config), @@ -121,7 +227,7 @@ attach(Config) -> {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo attach", "&"])), timer:sleep(2000), {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), - {error, 1} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). pid(Config) -> LibDir1 = proplists:get_value(lib1, Config), @@ -154,7 +260,7 @@ pid(Config) -> {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), {ok, _Pid} = sh(filename:join([OutputDir, "foo", "bin", "foo pid"])), {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), - {error, 1} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). restart(Config) -> LibDir1 = proplists:get_value(lib1, Config), @@ -191,7 +297,7 @@ restart(Config) -> timer:sleep(2000), {ok, Pid2} = sh(filename:join([OutputDir, "foo", "bin", "foo pid"])), {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), - {error, 1} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), ?assertEqual(Pid1, Pid2). reboot(Config) -> @@ -231,7 +337,7 @@ reboot(Config) -> timer:sleep(2000), {ok, Pid2} = sh(filename:join([OutputDir, "foo", "bin", "foo pid"])), {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), - {error, 1} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), ?assertNotEqual(Pid1, Pid2). escript(Config) -> @@ -307,6 +413,1118 @@ remote_console(Config) -> ?assertEqual(1, length(Nodes)), {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])). +replace_os_vars(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"]), + SysConfig = filename:join([LibDir1, "sys.config"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {sys_config, SysConfig}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + rlx_test_utils:write_config(SysConfig, + [[{goal_app, [{var1, "${VAR1}"}]}]]), + ec_file:write(VmArgs, "-sname ${NODENAME}\n\n" + "-setcookie ${COOKIE}\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}, + {"VAR1", "v1"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"v1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"node1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "cookie1"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, _Node1} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + + %% start the node again but this time with different env variables to replace + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}, + {"VAR1", "v2"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"v2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"node2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "cookie2"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, _Node2} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + ok. + +replace_os_vars_multi_node(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"]), + SysConfig = filename:join([LibDir1, "sys.config"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {sys_config, SysConfig}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + rlx_test_utils:write_config(SysConfig, + [[{goal_app, [{var1, "${VAR1}"}]}]]), + ec_file:write(VmArgs, "-sname ${NODENAME}\n\n" + "-setcookie ${COOKIE}\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}, + {"VAR1", "v1"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"v1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"node1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "cookie1"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, Node1} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys." ++ + rlx_test_utils:unescape_string(Node1) ++ + ".config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + + %% start the node again but this time with different env variables to replace + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}, + {"VAR1", "v2"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"v2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"node2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "cookie2"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, Node2} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys." ++ + rlx_test_utils:unescape_string(Node2) ++ + ".config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_MULTI_NODE", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + ok. + +replace_os_vars_included_config(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"]), + SysConfig = filename:join([LibDir1, "sys.config"]), + IncludedConfig = filename:join([LibDir1, "config", "included.config"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {sys_config, SysConfig}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true}, + {overlay, [ + {mkdir, "releases/{{release_version}}/config"}, + {template, "config/included.config", "releases/{{release_version}}/config/included.config"} + ]} + ]), + + rlx_test_utils:write_config(IncludedConfig, + [[{goal_app, [ + {var1_included, "${VAR1}"}] + }] + ]), + rlx_test_utils:write_config(SysConfig, + [[{goal_app, [ + {var1, "${VAR1}"}] + }, + "releases/0.0.1/config/included.config"] + ]), + ec_file:write(VmArgs, "-sname ${NODENAME}\n\n" + "-setcookie ${COOKIE}\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}, + {"VAR1", "v1"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"v1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"node1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "cookie1"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, _Node1} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + + %% start the node again but this time with different env variables to replace + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}, + {"VAR1", "v2"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"v2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"node2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "cookie2"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, _Node2} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + ok. + +replace_os_vars_custom_location(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"]), + SysConfig = filename:join([LibDir1, "sys.config"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {sys_config, SysConfig}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + rlx_test_utils:write_config(SysConfig, + [[{goal_app, [{var1, "${VAR1}"}]}]]), + ec_file:write(VmArgs, "-sname ${NODENAME}\n\n" + "-setcookie ${COOKIE}\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}, + {"VAR1", "v1"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"v1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"node1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "cookie1"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, _Node1} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join(["/", "tmp", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + + %% start the node again but this time with different env variables to replace + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}, + {"VAR1", "v2"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"v2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"node2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "cookie2"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, _Node2} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join(["/", "tmp", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"RELX_OUT_FILE_PATH", "/tmp"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + ok. + +replace_os_vars_twice(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"]), + SysConfig = filename:join([LibDir1, "sys.config"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {sys_config, SysConfig}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + rlx_test_utils:write_config(SysConfig, + [[{goal_app, [{var1, "${VAR1}"}]}]]), + ec_file:write(VmArgs, "-sname node\n\n" + "-setcookie cookie\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"VAR1", "v1"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}]), + {ok, "\"v1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}]), + {ok, "\"node\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}]), + {ok, "cookie"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}]), + {ok, _Node1} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}]), + + %% start the node again but this time don't replace env variables + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"])), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + {ok, "\"${VAR1}\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"])), + {ok, "\"node\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"])), + {ok, "cookie"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"])), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), + ok. + +replace_os_vars_dev_mode(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"]), + SysConfig = filename:join([LibDir1, "sys.config"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {sys_config, SysConfig}, + {vm_args, VmArgs}, + {dev_mode, true}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + rlx_test_utils:write_config(SysConfig, + [[{goal_app, [{var1, "${VAR1}"}]}]]), + ec_file:write(VmArgs, "-sname ${NODENAME}\n\n" + "-setcookie ${COOKIE}\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}, + {"VAR1", "v1"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"v1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "\"node1\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, "cookie1"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + {ok, _Node1} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node1"}, + {"COOKIE", "cookie1"}]), + + %% start the node again but this time with different env variables to replace + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}, + {"VAR1", "v2"}]), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"v2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '{ok, V} = application:get_env(goal_app, var1), V.'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "\"node2\""} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval '[Node,_] = re:split(atom_to_list(node()), \"@\"),binary_to_list(Node).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, "cookie2"} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'erlang:get_cookie().'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + {ok, _Node2} = sh(filename:join([OutputDir, "foo", "bin", + "foo eval 'atom_to_list(node()).'"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + %% check that the replaced files have been created in the right place + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys.config"]))), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"]), + [{"RELX_REPLACE_OS_VARS", "1"}, + {"NODENAME", "node2"}, + {"COOKIE", "cookie2"}]), + ok. + +custom_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, [ + {custom, "hooks/pre_start"} + ]}, + {post_start, [ + {custom, "hooks/post_start"} + ]}, + {pre_stop, [ + {custom, "hooks/pre_stop"} + ]}, + {post_stop, [ + {custom, "hooks/post_stop"} + ]} + ]}, + {mkdir, "scripts"}, + {overlay, [{copy, "./pre_start", "bin/hooks/pre_start"}, + {copy, "./post_start", "bin/hooks/post_start"}, + {copy, "./pre_stop", "bin/hooks/pre_stop"}, + {copy, "./post_stop", "bin/hooks/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"])). + +builtin_pid_start_script_hook(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"]), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + 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, [ + {post_start, [ + {pid, filename:join([OutputDir, "foo.pid"])} + ]} + ]} + ]), + + {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"])), + %% check that the pid file really was created + ?assert(ec_file:exists(filename:join([OutputDir, "foo.pid"]))), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo stop"])), + ok. + +builtin_wait_for_vm_start_script_hook(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, [ + {post_start, [wait_for_vm_start]} + ]} + ]), + + 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"])), + % this run doesn't need the sleep because the wait_for_vm_start + % start script makes it unnecessary + %timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo stop"])), + ok. + +builtin_wait_for_process_start_script_hook(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_full_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, [ + {post_start, [wait_for_vm_start, + {wait_for_process, goal_app_srv_signal}]} + ]} + ]), + + 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 + %% get the current time, we'll measure how long it took for the node to + %% start, it must be at least 3 seconds which is the time it takes the + %% goal_app_srv to register the signal + T1 = os:timestamp(), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo start"])), + T2 = timer:now_diff(os:timestamp(), T1), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo stop"])), + ?assert((T2 div 1000) > 3000), + ok. + +mixed_custom_and_builtin_start_script_hooks(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_full_app(LibDir1, "goal_app", "0.0.1", + [stdlib,kernel], []), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + 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, [ + {custom, "hooks/pre_start"} + ]}, + {post_start, [ + wait_for_vm_start, + {pid, filename:join([OutputDir, "foo.pid"])}, + {wait_for_process, goal_app_srv_signal}, + {custom, "hooks/post_start"} + ]}, + {pre_stop, [ + {custom, "hooks/pre_stop"} + ]}, + {post_stop, [ + {custom, "hooks/post_stop"} + ]} + ]}, + {mkdir, "scripts"}, + {overlay, [{copy, "./pre_start", "bin/hooks/pre_start"}, + {copy, "./post_start", "bin/hooks/post_start"}, + {copy, "./pre_stop", "bin/hooks/pre_stop"}, + {copy, "./post_stop", "bin/hooks/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"), + + {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 + %% get the current time, we'll measure how long it took for the node to + %% start, it must be at least 3 seconds which is the time it takes the + %% goal_app_srv to register the signal + T1 = os:timestamp(), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo start"])), + % this run doesn't need the sleep because the wait_for_vm_start + % start script makes it unnecessary + T2 = timer:now_diff(os:timestamp(), T1), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + ?assert((T2 div 1000) > 3000), + %% check that the pid file really was created + ?assert(ec_file:exists(filename:join([OutputDir, "foo.pid"]))), + 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"])). + +builtin_status_script(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_full_app(LibDir1, "goal_app", "0.0.1", + [stdlib,kernel], []), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + 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} + ]), + + {ok, _State} = relx:do(foo, undefined, [], [LibDir1], 3, + OutputDir, ConfigFile), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo start"])), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + %% write the status to a file + {ok, ""} = sh(filename:join([OutputDir, "foo", "bin", "foo status"])). + +custom_status_script(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_full_app(LibDir1, "goal_app", "0.0.1", + [stdlib,kernel], []), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + 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, [ + {status, [ + {custom, "hooks/status"} + ]} + ]}, + {overlay, [ + {copy, "./status", "bin/hooks/status"}]} + ]), + + %% write the hook status script + ok = file:write_file(filename:join([LibDir1, "./status"]), + "#!/bin/bash\n# $*\necho \\{status, $REL_NAME, \\'$NAME\\', $COOKIE\\}."), + + {ok, _State} = relx:do(foo, undefined, [], [LibDir1], 3, + OutputDir, ConfigFile), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo start"])), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + %% write the status to a file + {ok, StatusStr} = sh(filename:join([OutputDir, "foo", "bin", "foo status"])), + ec_file:write(filename:join([OutputDir, "status.txt"]), StatusStr), + os:cmd(filename:join([OutputDir, "foo", "bin", "foo stop"])), + {ok, [Status]} = file:consult(filename:join([OutputDir, "status.txt"])), + {ok, {status, foo, _, foo} = Status}. + +start_sname_in_other_argsfile(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"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + VmArgs2 = VmArgs ++ ".2", + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + ec_file:write(VmArgs, "-args_file " ++ VmArgs2 ++ "\n\n" + "-setcookie cookie\n"), + + ec_file:write(VmArgs2, "-sname foo\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + %% now start/stop the release to make sure the extended script is working + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"])), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), + %% a ping should fail after stopping a node + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). + +start_fail_when_no_name(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + ec_file:write(VmArgs, "-setcookie cookie\n"), + start_fail_with_vmargs(Config, VmArgs, 1). + +start_fail_when_multiple_names(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + ec_file:write(VmArgs, "-name foo\n\n" + "-name bar\n\n" + "-setcookie cookie\n"), + start_fail_with_vmargs(Config, VmArgs, 2). + +start_fail_when_missing_argsfile(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + ec_file:write(VmArgs, "-name foo\n\n" + "-args_file " ++ VmArgs ++ ".nonexistent\n\n" + "-setcookie cookie\n"), + start_fail_with_vmargs(Config, VmArgs, 3). + +start_fail_when_nonreadable_argsfile(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + VmArgs2 = VmArgs ++ ".nonreadable", + ec_file:write(VmArgs, "-name foo\n\n" + "-args_file " ++ VmArgs2 ++ "\n\n" + "-setcookie cookie\n"), + ec_file:write(VmArgs2, ""), + file:change_mode(VmArgs2, 8#00333), + start_fail_with_vmargs(Config, VmArgs, 3). + +start_fail_when_relative_argsfile(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + ec_file:write(VmArgs, "-name foo\n\n" + "-args_file vm.args.relative\n\n" + "-setcookie cookie\n"), + start_fail_with_vmargs(Config, VmArgs, 4). + +start_fail_when_circular_argsfiles(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + VmArgs2 = VmArgs ++ ".2", + VmArgs3 = VmArgs ++ ".3", + ec_file:write(VmArgs, "-name foo\n\n" + "-args_file " ++ VmArgs2 ++ "\n\n" + "-setcookie cookie\n"), + ec_file:write(VmArgs2, "-args_file " ++ VmArgs3 ++ "\n"), + ec_file:write(VmArgs3, "-args_file " ++ VmArgs2 ++ "\n"), + start_fail_with_vmargs(Config, VmArgs, 5). + +%%------------------------------------------------------------------- +%% Helper Function for start_fail_when_* tests +%%------------------------------------------------------------------- +start_fail_with_vmargs(Config, VmArgs, ExpectedCode) -> + 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, "*")]}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + %% now start/stop the release to make sure the extended script is working + {error, ExpectedCode, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"])). + %%%=================================================================== %%% Helper Functions %%%=================================================================== @@ -327,8 +1545,8 @@ sh(Command, Env, Dir) -> case sh_loop(Port) of {ok, Ret} -> {ok, Ret}; - {error, Rc} -> - {error, Rc} + {error, Rc, Msg} -> + {error, Rc, Msg} end. sh_loop(Port) -> @@ -341,7 +1559,7 @@ sh_loop(Port, Acc) -> {Port, {exit_status, 0}} -> {ok, Acc}; {Port, {exit_status, Rc}} -> - {error, Rc} + {error, Rc, Acc} end. get_cwd() -> diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl index 2b30923..c9430fd 100644 --- a/test/rlx_release_SUITE.erl +++ b/test/rlx_release_SUITE.erl @@ -46,12 +46,17 @@ make_relup_release2/1, make_one_app_top_level_release/1, make_dev_mode_release/1, + make_dev_mode_template_release/1, make_config_script_release/1, make_release_twice/1, make_release_twice_dev_mode/1, make_erts_release/1, make_erts_config_release/1, - make_included_nodetool_release/1]). + make_included_nodetool_release/1, + make_not_included_nodetool_release/1, + make_src_release/1, + make_excluded_src_release/1, + make_exclude_modules_release/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -83,9 +88,11 @@ all() -> make_implicit_config_release, make_rerun_overridden_release, overlay_release, make_goalless_release, make_depfree_release, make_invalid_config_release, make_relup_release, make_relup_release2, - make_one_app_top_level_release, make_dev_mode_release, + make_one_app_top_level_release, make_dev_mode_release, make_dev_mode_template_release, make_config_script_release, make_release_twice, make_release_twice_dev_mode, - make_erts_release, make_erts_config_release, make_included_nodetool_release]. + make_erts_release, make_erts_config_release, + make_included_nodetool_release, make_not_included_nodetool_release, + make_src_release, make_excluded_src_release, make_exclude_modules_release]. add_providers(Config) -> LibDir1 = proplists:get_value(lib1, Config), @@ -1007,9 +1014,9 @@ make_dev_mode_release(Config) -> ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "lib", "goal_app_2-0.0.1"]))), ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "lib", "lib_dep_1-0.0.1"]))), ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "releases", "0.0.1", - "sys.config.orig"]))), + "sys.config"]))), ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "releases", "0.0.1", - "vm.args.orig"]))); + "vm.args"]))); {win32, _} -> ?assert(filelib:is_dir(filename:join([OutputDir, "foo", "lib", "non_goal_1-0.0.1"]))), ?assert(filelib:is_dir(filename:join([OutputDir, "foo", "lib", "non_goal_2-0.0.1"]))), @@ -1022,6 +1029,66 @@ make_dev_mode_release(Config) -> "vm.args"]))) end. +make_dev_mode_template_release(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", + [stdlib,kernel,non_goal_1], []), + rlx_test_utils:create_app(LibDir1, "lib_dep_1", "0.0.1", + [stdlib,kernel], []), + rlx_test_utils:create_app(LibDir1, "goal_app_2", "0.0.1", + [stdlib,kernel,goal_app_1,non_goal_2], []), + rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", + [stdlib,kernel], [lib_dep_1]), + rlx_test_utils:create_app(LibDir1, "non_goal_2", "0.0.1", + [stdlib,kernel], []), + + SysConfig = filename:join([LibDir1, "config", "sys.config"]), + SysConfigTerm = [{this_is_a_test, "yup it is"}, + {this_is_an_overlay_var, "{{var1}}"}], + rlx_test_utils:write_config(SysConfig, SysConfigTerm), + + VmArgs = filename:join([LibDir1, "config", "vm.args"]), + ec_file:write(VmArgs, "-sname {{nodename}}"), + + VarsFile1 = filename:join([LibDir1, "config", "vars1.config"]), + rlx_test_utils:write_config(VarsFile1, [{var1, "indeed it is"}, + {nodename, "testnode"}]), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{dev_mode, true}, + {sys_config, SysConfig}, + {vm_args, VmArgs}, + {overlay_vars, [VarsFile1]}, + {overlay, [ + {template, "config/sys.config", + "releases/{{release_version}}/sys.config"}, + {template, "config/vm.args", + "releases/{{release_version}}/vm.args"}]}, + {release, {foo, "0.0.1"}, + [goal_app_1, + goal_app_2]}]), + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, + OutputDir, ConfigFile), + [{{foo, "0.0.1"}, _Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), + + ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "lib", "non_goal_1-0.0.1"]))), + ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "lib", "non_goal_2-0.0.1"]))), + ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "lib", "goal_app_1-0.0.1"]))), + ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "lib", "goal_app_2-0.0.1"]))), + ?assert(ec_file:is_symlink(filename:join([OutputDir, "foo", "lib", "lib_dep_1-0.0.1"]))), + ?assert(not ec_file:is_symlink(filename:join([OutputDir, "foo", "releases", "0.0.1", + "sys.config"]))), + ?assert(not ec_file:is_symlink(filename:join([OutputDir, "foo", "releases", "0.0.1", + "vm.args"]))), + %% ensure that the original sys.config didn't get overwritten + ?assertMatch({ok, SysConfigTerm}, file:consult(SysConfig)), + %% ensure that the original vm.args didn't get overwritten + ?assertMatch({ok, <<"-sname {{nodename}}">>}, ec_file:read(VmArgs)). + make_config_script_release(Config) -> LibDir1 = proplists:get_value(lib1, Config), FooRoot = filename:join([LibDir1, "foodir1", "foodir2"]), @@ -1243,10 +1310,138 @@ make_included_nodetool_release(Config) -> OutputDir, ConfigFile), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), AppSpecs = rlx_release:applications(Release), + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "bin", "nodetool"]))), + ?assert(lists:keymember(stdlib, 1, AppSpecs)), + ?assert(lists:keymember(kernel, 1, AppSpecs)), + ?assertEqual(ErtsVsn, rlx_release:erts(Release)). + +make_not_included_nodetool_release(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "lib_dep_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "goal_app_2", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "non_goal_2", "0.0.1", [kernel,stdlib], []), + + ErtsVsn = erlang:system_info(version), + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, {erts, ErtsVsn}, + [goal_app_1]}, + {extended_start_script, true}, + {include_nodetool, false}]), + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, + OutputDir, ConfigFile), + [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), + AppSpecs = rlx_release:applications(Release), + %% extended start script needs nodetool to work, so the + %% {include_nodetool, false} option is simply ignored + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "bin", "nodetool"]))), ?assert(lists:keymember(stdlib, 1, AppSpecs)), ?assert(lists:keymember(kernel, 1, AppSpecs)), ?assertEqual(ErtsVsn, rlx_release:erts(Release)). +make_src_release(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "lib_dep_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "goal_app_2", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "non_goal_2", "0.0.1", [kernel,stdlib], []), + + ErtsVsn = erlang:system_info(version), + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app_1]}, + {extended_start_script, true}, + {include_erts, true}, + {include_src, true}]), + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, + OutputDir, ConfigFile), + [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), + AppSpecs = rlx_release:applications(Release), + ?assert(lists:keymember(stdlib, 1, AppSpecs)), + ?assert(lists:keymember(kernel, 1, AppSpecs)), + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "erts-"++ErtsVsn, "src"]))), + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "lib", + "goal_app_1-0.0.1", "src"]))). + +make_excluded_src_release(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "lib_dep_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "goal_app_2", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [kernel,stdlib], []), + rlx_test_utils:create_app(LibDir1, "non_goal_2", "0.0.1", [kernel,stdlib], []), + + ErtsVsn = erlang:system_info(version), + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app_1]}, + {extended_start_script, true}, + {include_erts, true}, + {include_src, false}]), + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, + OutputDir, ConfigFile), + [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), + AppSpecs = rlx_release:applications(Release), + ?assert(lists:keymember(stdlib, 1, AppSpecs)), + ?assert(lists:keymember(kernel, 1, AppSpecs)), + ?assert(not ec_file:exists(filename:join([OutputDir, "foo", "erts-"++ErtsVsn, "src"]))), + ?assert(not ec_file:exists(filename:join([OutputDir, "foo", "lib", + "goal_app_1-0.0.1", "src"]))). + +%% Test to ensure that excluded modules don't end up in the release +make_exclude_modules_release(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [stdlib,kernel, non_goal_1], []), + rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [stdlib,kernel], []), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app_1]}, + {exclude_modules, [{non_goal_1, [a_real_beamnon_goal_1]}]}]), + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, Cwd} = file:get_cwd(), + {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 3, + OutputDir, [], + ConfigFile), + [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), + AppSpecs = rlx_release:applications(Release), + ?assert(lists:keymember(stdlib, 1, AppSpecs)), + ?assert(lists:keymember(kernel, 1, AppSpecs)), + ?assert(lists:member({goal_app_1, "0.0.1"}, AppSpecs)), + %% ensure that the excluded module beam file didn't get copied + ?assert(not ec_file:exists(filename:join([OutputDir, "foo", "lib", + "non_goal_1-0.0.1", "ebin", + "a_real_beamnon_goal_1.beam"]))), + + ?assertMatch({ok, [{application,non_goal_1, + [{description,[]}, + {vsn,"0.0.1"}, + {modules,[]}, + {included_applications,[]}, + {registered,[]}, + {applications,[stdlib,kernel]}]}]}, + file:consult(filename:join([OutputDir, "foo", "lib", + "non_goal_1-0.0.1", "ebin", + "non_goal_1.app"]))). + + %%%=================================================================== %%% Helper Functions %%%=================================================================== diff --git a/test/rlx_test_utils.erl b/test/rlx_test_utils.erl index 2e6045f..37d9c7a 100644 --- a/test/rlx_test_utils.erl +++ b/test/rlx_test_utils.erl @@ -6,21 +6,33 @@ create_app(Dir, Name, Vsn, Deps, LibDeps) -> AppDir = filename:join([Dir, Name ++ "-" ++ Vsn]), - write_app_file(AppDir, Name, Vsn, Deps, LibDeps), - write_beam_file(AppDir, Name), + write_app_file(AppDir, Name, Vsn, app_modules(Name), Deps, LibDeps), + write_src_file(AppDir, Name), + compile_src_files(AppDir), + rlx_app_info:new(erlang:list_to_atom(Name), Vsn, AppDir, + Deps, []). + +create_full_app(Dir, Name, Vsn, Deps, LibDeps) -> + AppDir = filename:join([Dir, Name ++ "-" ++ Vsn]), + write_full_app_files(AppDir, Name, Vsn, Deps, LibDeps), + compile_src_files(AppDir), rlx_app_info:new(erlang:list_to_atom(Name), Vsn, AppDir, Deps, []). create_empty_app(Dir, Name, Vsn, Deps, LibDeps) -> AppDir = filename:join([Dir, Name ++ "-" ++ Vsn]), - write_app_file(AppDir, Name, Vsn, Deps, LibDeps), + write_app_file(AppDir, Name, Vsn, [], Deps, LibDeps), rlx_app_info:new(erlang:list_to_atom(Name), Vsn, AppDir, Deps, []). -write_beam_file(Dir, Name) -> - Beam = filename:join([Dir, "ebin", "not_a_real_beam" ++ Name ++ ".beam"]), - ok = filelib:ensure_dir(Beam), - ok = ec_file:write_term(Beam, testing_purposes_only). +app_modules(Name) -> + [list_to_atom(M ++ Name) || + M <- ["a_real_beam"]]. + +write_src_file(Dir, Name) -> + Src = filename:join([Dir, "src", "a_real_beam" ++ Name ++ ".erl"]), + ok = filelib:ensure_dir(Src), + ok = file:write_file(Src, beam_file_contents("a_real_beam"++Name)). write_appup_file(AppInfo, DownVsn) -> Dir = rlx_app_info:dir(AppInfo), @@ -30,20 +42,112 @@ write_appup_file(AppInfo, DownVsn) -> ok = filelib:ensure_dir(Filename), ok = ec_file:write_term(Filename, {Vsn, [{DownVsn, []}], [{DownVsn, []}]}). -write_app_file(Dir, Name, Version, Deps, LibDeps) -> +write_app_file(Dir, Name, Version, Modules, Deps, LibDeps) -> Filename = filename:join([Dir, "ebin", Name ++ ".app"]), ok = filelib:ensure_dir(Filename), - ok = ec_file:write_term(Filename, get_app_metadata(Name, Version, Deps, LibDeps)). + ok = ec_file:write_term(Filename, get_app_metadata(Name, Version, Modules, + Deps, LibDeps)). + +compile_src_files(Dir) -> + %% compile all *.erl files in src to ebin + SrcDir = filename:join([Dir, "src"]), + OutputDir = filename:join([Dir, "ebin"]), + lists:foreach(fun(SrcFile) -> + {ok, _} = compile:file(SrcFile, [{outdir, OutputDir}, + return_errors]) + end, ec_file:find(SrcDir, "\\.erl")), + ok. -get_app_metadata(Name, Vsn, Deps, LibDeps) -> +get_app_metadata(Name, Vsn, Modules, Deps, LibDeps) -> {application, erlang:list_to_atom(Name), [{description, ""}, {vsn, Vsn}, - {modules, []}, + {modules, Modules}, {included_applications, LibDeps}, {registered, []}, {applications, Deps}]}. +write_full_app_files(Dir, Name, Vsn, Deps, LibDeps) -> + %% write out the .app file + AppFilename = filename:join([Dir, "ebin", Name ++ ".app"]), + ok = filelib:ensure_dir(AppFilename), + ok = ec_file:write_term(AppFilename, + get_full_app_metadata(Name, Vsn, Deps, LibDeps)), + %% write out the _app.erl file + ApplicationFilename = filename:join([Dir, "src", Name ++ "_app.erl"]), + ok = filelib:ensure_dir(ApplicationFilename), + ok = file:write_file(ApplicationFilename, full_application_contents(Name)), + %% write out the supervisor + SupervisorFilename = filename:join([Dir, "src", Name ++ "_sup.erl"]), + ok = filelib:ensure_dir(SupervisorFilename), + ok = file:write_file(SupervisorFilename, supervisor_contents(Name)), + %% and finally the gen_server + GenServerFilename = filename:join([Dir, "src", Name ++ "_srv.erl"]), + ok = filelib:ensure_dir(GenServerFilename), + ok = file:write_file(GenServerFilename, gen_server_contents(Name)), + ok. + +get_full_app_metadata(Name, Vsn, Deps, LibDeps) -> + {application, erlang:list_to_atom(Name), + [{description, ""}, + {vsn, Vsn}, + {modules, [goal_app_app,goal_app_sup,goal_app_srv]}, + {mod, {erlang:list_to_atom(Name ++ "_app"), + []}}, + {included_applications, LibDeps}, + {registered, []}, + {applications, Deps}]}. + +full_application_contents(Name) -> + "-module("++Name++"_app).\n" + "-behaviour(application).\n" + "-export([start/2, stop/1]).\n" + "start(_StartType, _StartArgs) ->\n" + " "++Name++"_sup:start_link().\n" + "stop(_State) ->\n" + " ok.\n". + +supervisor_contents(Name) -> + "-module("++Name++"_sup).\n" + "-behaviour(supervisor).\n" + "-export([start_link/0]).\n" + "-export([init/1]).\n" + "-define(SERVER, ?MODULE).\n" + "start_link() ->\n" + " supervisor:start_link({local, ?SERVER}, ?MODULE, []).\n" + "init([]) ->\n" + " {ok, { {one_for_all, 0, 1},\n" + " [{"++Name++"_srv, {"++Name++"_srv, start_link, []},\n" + " transient, 5000, worker, ["++Name++"_srv]}\n" + " ]\n" + " }}.\n". + +gen_server_contents(Name) -> + "-module("++Name++"_srv).\n" + "-behaviour(gen_server).\n" + "-record(state, {}).\n" + "-export([start_link/0]).\n" + "-export([init/1,handle_call/3,handle_cast/2,\n" + " handle_info/2,terminate/2,code_change/3]).\n" + "start_link() ->\n" + " gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).\n" + "init([]) ->\n" + " erlang:send_after(4000, self(), register_signal)," + " {ok, #state{}}.\n" + "handle_call(_Event, _From, State) ->\n" + " {reply, ok, State}.\n" + "handle_cast(_Event, State) ->\n" + " {noreply, State}.\n" + "handle_info(register_signal, State) ->\n" + " erlang:register(goal_app_srv_signal, spawn(fun() -> timer:sleep(200000) end)),\n" + " {noreply, State};\n" + "handle_info(_Info, State) ->\n" + " {noreply, State}.\n" + "terminate(_Reason, _State) ->\n" + " ok.\n" + "code_change(_OldVsn, State, _Extra) ->\n" + " {ok, State}.\n". + create_random_name(Name) -> Name ++ erlang:integer_to_list(random_uniform(1000000)). @@ -57,6 +161,9 @@ write_config(Filename, Values) -> ok = ec_file:write(Filename, [io_lib:format("~p.\n", [Val]) || Val <- Values]). +beam_file_contents(Name) -> + "-module("++Name++").". + test_template_contents() -> "{erts_vsn, \"{{erts_vsn}}\"}.\n" "{release_erts_version, \"{{release_erts_version}}\"}.\n" @@ -104,3 +211,7 @@ list_to_term(String) -> {error, Error} -> Error end. + +unescape_string(String) -> + re:replace(String, "\"", "", + [global, {return, list}]). |