aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/kernel/src/kernel.appup.src28
-rw-r--r--lib/kernel/test/kernel_SUITE.erl64
-rw-r--r--lib/sasl/doc/src/appup.xml37
-rw-r--r--lib/sasl/doc/src/release_handler.xml28
-rw-r--r--lib/sasl/doc/src/systools.xml5
-rw-r--r--lib/sasl/src/release_handler.erl420
-rw-r--r--lib/sasl/src/release_handler_1.erl120
-rw-r--r--lib/sasl/src/sasl.appup.src26
-rw-r--r--lib/sasl/src/systools_make.erl144
-rw-r--r--lib/sasl/src/systools_rc.erl40
-rw-r--r--lib/sasl/src/systools_relup.erl153
-rw-r--r--lib/sasl/test/Makefile4
-rw-r--r--lib/sasl/test/installer.erl576
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl346
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl4
-rw-r--r--lib/sasl/test/sasl_SUITE.erl99
-rw-r--r--lib/sasl/test/systools_SUITE.erl690
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.appup28
-rw-r--r--lib/sasl/test/systools_SUITE_data/lib/sasl-9.9/ebin/sasl.app6
-rw-r--r--lib/sasl/test/systools_SUITE_data/lib/sasl-9.9/ebin/sasl.appup12
-rw-r--r--lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.app6
-rw-r--r--lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.appup12
-rw-r--r--lib/sasl/test/systools_rc_SUITE.erl92
-rw-r--r--lib/sasl/test/test_lib.hrl3
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/stdlib/src/stdlib.appup.src28
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl63
-rw-r--r--system/doc/design_principles/appup_cookbook.xml61
-rw-r--r--system/doc/design_principles/release_handling.xml52
35 files changed, 2542 insertions, 646 deletions
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index 54a63833e6..bded2408a7 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -1 +1,27 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+{"%VSN%",
+ %% Up from - max two major revisions back
+ [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ %% Down to - max two major revisions back
+ [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+}.
diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl
index 16b6c54939..0f29d895e5 100644
--- a/lib/kernel/test/kernel_SUITE.erl
+++ b/lib/kernel/test/kernel_SUITE.erl
@@ -32,7 +32,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
--export([app_test/1]).
+-export([app_test/1, appup_test/1]).
%%
%% all/1
@@ -40,7 +40,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test].
+ [app_test, appup_test].
groups() ->
[].
@@ -76,3 +76,63 @@ app_test(suite) ->
app_test(Config) when is_list(Config) ->
?line ok=?t:app_test(kernel),
ok.
+
+
+%% Test that appup allows upgrade from/downgrade to a maximum of two
+%% major releases back.
+appup_test(_Config) ->
+ application:load(kernel),
+ {_,_,Vsn} = lists:keyfind(kernel,1,application:loaded_applications()),
+ AppupFile = filename:join([code:lib_dir(kernel),ebin,"kernel.appup"]),
+ {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
+ ct:log("~p~n",[AppupScript]),
+ {OkVsns,NokVsns} = create_test_vsns(Vsn),
+ check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
+ check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
+ check_appup(NokVsns,UpFrom,error),
+ check_appup(NokVsns,DownTo,error),
+ ok.
+
+create_test_vsns(Current) ->
+ [XStr,YStr|Rest] = string:tokens(Current,"."),
+ X = list_to_integer(XStr),
+ Y = list_to_integer(YStr),
+ SecondMajor = vsn(X,Y-2),
+ SecondMinor = SecondMajor ++ ".1.3",
+ FirstMajor = vsn(X,Y-1),
+ FirstMinor = FirstMajor ++ ".57",
+ ThisMajor = vsn(X,Y),
+ This =
+ case Rest of
+ [] ->
+ [];
+ ["1"] ->
+ [ThisMajor];
+ _ ->
+ ThisMinor = ThisMajor ++ ".1",
+ [ThisMajor,ThisMinor]
+ end,
+ OkVsns = This ++ [FirstMajor, FirstMinor, SecondMajor, SecondMinor],
+
+ ThirdMajor = vsn(X,Y-3),
+ ThirdMinor = ThirdMajor ++ ".10.12",
+ Illegal = ThisMajor ++ ",1",
+ Newer1Major = vsn(X,Y+1),
+ Newer1Minor = Newer1Major ++ ".1",
+ Newer2Major = ThisMajor ++ "1",
+ NokVsns = [ThirdMajor,ThirdMinor,
+ Illegal,
+ Newer1Major,Newer1Minor,
+ Newer2Major],
+ {OkVsns,NokVsns}.
+
+vsn(X,Y) ->
+ integer_to_list(X) ++ "." ++ integer_to_list(Y).
+
+check_appup([Vsn|Vsns],Instrs,Expected) ->
+ case systools_relup:appup_search_for_version(Vsn, Instrs) of
+ Expected -> check_appup(Vsns,Instrs,Expected);
+ Other -> ct:fail({unexpected_result_for_vsn,Vsn,Other})
+ end;
+check_appup([],_,_) ->
+ ok.
diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml
index 89bcf23b5e..195f9fe1d3 100644
--- a/lib/sasl/doc/src/appup.xml
+++ b/lib/sasl/doc/src/appup.xml
@@ -319,12 +319,37 @@ point_of_no_return
<pre>
restart_new_emulator
</pre>
- <p>Shuts down the current emulator and starts a ne one. All
- processes are terminated gracefully. The new release must still
- be made permanent when the new emulator is up and running.
- Otherwise, the old emulator is started in case of a emulator
- restart. This instruction should be used when a new emulator is
- introduced, or if a complete reboot of the system should be done.</p>
+ <p>This instruction is used when erts, kernel, stdlib or sasl is
+ upgraded. It shuts down the current emulator and starts a new
+ one. All processes are terminated gracefully, and the new
+ version of erts, kernel, stdlib and sasl are used when the
+ emulator restarts. Only one <c>restart_new_emulator</c>
+ instruction is allowed in the relup, and it shall be placed
+ first. <seealso marker="systools#make_relup/3">systools:make_relup3,4</seealso>
+ will ensure this when the relup is generated. The rest of the
+ relup script is executed after the restart as a part of the boot
+ script.</p>
+ <p>An info report will be written when the upgrade is
+ completed. To programatically find out if the upgrade is
+ complete,
+ call <seealso marker="release_handler#which_release/0">
+ release_handler:which_releases</seealso> and check if the
+ expected release has status <c>current</c>.</p>
+ <p>The new release must still be made permanent after the upgrade
+ is completed. Otherwise, the old emulator is started in case of
+ an emulator restart.</p>
+ <pre>
+restart_emulator
+ </pre>
+ <p>This instruction is similar to <c>restart_new_emulator</c>,
+ except it shall be placed at the end of the relup script. It is
+ not related to an upgrade of the emulator or the core
+ applications, but can be used by any application when a complete
+ reboot of the system is reqiured. When generating the
+ relup, <seealso marker="systools#make_relup/3">systools:make_relup/3,4</seealso>
+ ensures that there is only one <c>restart_emulator</c>
+ instruction and that it is the last instruction of the
+ relup.</p>
</section>
<section>
diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml
index cf05b1a67d..7f32100d4b 100644
--- a/lib/sasl/doc/src/release_handler.xml
+++ b/lib/sasl/doc/src/release_handler.xml
@@ -238,7 +238,7 @@ old reboot_old permanent
</func>
<func>
<name>install_release(Vsn) -> {ok, OtherVsn, Descr} | {error, Reason}</name>
- <name>install_release(Vsn, [Opt]) -> {ok, OtherVsn, Descr} | {error, Reason}</name>
+ <name>install_release(Vsn, [Opt]) -> {ok, OtherVsn, Descr} | {continue_after_restart, OtherVsn, Descr} | {error, Reason}</name>
<fsummary>Install a release in the system.</fsummary>
<type>
<v>Vsn = OtherVsn = string()</v>
@@ -248,7 +248,8 @@ old reboot_old permanent
<v>&nbsp;Timeout = default | infinity | int()>0</v>
<v>&nbsp;Bool = boolean()</v>
<v>Descr = term()</v>
- <v>Reason = {illegal_option, Opt} | {already_installed, Vsn} | {change_appl_data, term()} | term()</v>
+ <v>Reason = {illegal_option, Opt} | {already_installed, Vsn} | {change_appl_data, term()} | {missing_base_app, OtherVsn, App} | {could_not_create_hybrid_boot, term()} | term()</v>
+ <v>App = atom()</v>
</type>
<desc>
<p>Installs the specified version <c>Vsn</c> of the release.
@@ -268,6 +269,15 @@ old reboot_old permanent
<c>OtherVsn</c> and <c>Descr</c> are the version
(<c>UpFromVsn</c> or <c>Vsn</c>) and description
(<c>Descr1</c> or <c>Descr2</c>) as specified in the script.</p>
+ <p>If <c>{continue_after_restart,OtherVsn,Descr}</c> is
+ returned, it means that the emulator will be restarted
+ before the upgrade instructions are executed. This will
+ happen if the emulator or any of the applications kernel,
+ stdlib or sasl are updated. The new version of the emulator
+ and these core applications will execute after the restart,
+ but for all other applications the old versions will be
+ started and the upgrade will be performed as normal by
+ executing the upgrade instructions.</p>
<p>If a recoverable error occurs, the function returns
<c>{error,Reason}</c> and the original application
specifications are restored. If a non-recoverable error
@@ -325,6 +335,18 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
upgrade, but it will allow checks and purge to be executed
in the background before the real upgrade is started.</p>
</note>
+ <note>
+ <p>When upgrading the emulator from a version older than OTP
+ R15, there will be an attempt to load new application beam
+ code into the old emulator. In some cases, the new beam
+ format can not be read by the old emulator, and so the code
+ loading will fail and terminate the complete upgrade. To
+ overcome this problem, the new application code should be
+ compiled with the old emulator. See <seealso
+ marker="doc/design_principles:appup_cookbook">Design
+ Principles</seealso> for more information about emulator
+ upgrade from pre OTP R15 versions.</p>
+ </note>
</desc>
</func>
<func>
@@ -454,7 +476,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
and evaluated exactly in the same way as <c>release_handler</c>
does.</p>
<warning>
- <p>These function is primarily intended for simplified testing of
+ <p>These functions are primarily intended for simplified testing
of <c>.appup</c> files. They are not run within the context of
the <c>release_handler</c> process. They must therefore
<em>not</em> be used together with calls to
diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml
index 8c1c327d74..fa2fcbf534 100644
--- a/lib/sasl/doc/src/systools.xml
+++ b/lib/sasl/doc/src/systools.xml
@@ -111,6 +111,11 @@
low-level instruction to restart the emulator is appended to
the relup scripts. This ensures that a complete reboot of
the system is done when the system is upgraded or downgraded.</p>
+ <p>If an upgrade includes a change from an emulator earlier
+ than OTP R15 to OTP R15 or later, the warning
+ <c>pre_R15_emulator_upgrade</c> is issued. See <seealso
+ marker="doc/design_principles:appup_cookbook">Design
+ Principles</seealso> for more information about this.</p>
<p>By default, errors and warnings are printed to tty and
the function returns <c>ok</c> or <c>error</c>. If the option
<c>silent</c> is provided, the function instead returns
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index 5650f1433d..4e8cb4628c 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.erl
@@ -26,9 +26,9 @@
create_RELEASES/1, create_RELEASES/2, create_RELEASES/4,
unpack_release/1,
check_install_release/1, check_install_release/2,
- install_release/1, install_release/2, remove_release/1,
- which_releases/0, which_releases/1, make_permanent/1,
- reboot_old_release/1,
+ install_release/1, install_release/2, new_emulator_upgrade/2,
+ remove_release/1, which_releases/0, which_releases/1,
+ make_permanent/1, reboot_old_release/1,
set_unpacked/2, set_removed/1, install_file/2]).
-export([upgrade_app/2, downgrade_app/2, downgrade_app/3,
upgrade_script/2, downgrade_script/3,
@@ -41,7 +41,7 @@
%% Internal exports, a client release_handler may call this functions.
-export([do_write_release/3, do_copy_file/2, do_copy_files/2,
do_copy_files/1, do_rename_files/1, do_remove_files/1,
- do_write_file/2, do_ensure_RELEASES/1]).
+ remove_file/1, do_write_file/2, do_ensure_RELEASES/1]).
-record(state, {unpurged = [],
root,
@@ -62,10 +62,11 @@
%% remove -
%% current make_permanent permanent
%% install other old
+%% restart node unpacked
%% remove -
%% permanent make other permanent old
%% install permanent
-%% old reboot permanen
+%% old reboot_old permanent
%% install current
%% remove -
%%-----------------------------------------------------------------
@@ -75,6 +76,14 @@
-define(timeout, 10000).
%%-----------------------------------------------------------------
+%% The version set on the temporary release that will be used when the
+%% emulator is upgraded.
+-define(tmp_vsn(__BaseVsn__), "__new_emulator__"++__BaseVsn__).
+
+
+
+
+%%-----------------------------------------------------------------
%% Assumes the following file structure:
%% root --- lib --- Appl-Vsn1 --- <src>
%% | | |- ebin
@@ -184,11 +193,15 @@ check_check_install_options([],Purge) ->
%%-----------------------------------------------------------------
%% Purpose: Executes the relup script for the specified version.
%% The release must be unpacked.
-%% Returns: {ok, FromVsn, Descr} | {error, Reason}
+%% Returns: {ok, FromVsn, Descr} |
+%% {continue_after_restart, FromVsn, Descr} |
+%% {error, Reason}
%% Reason = {already_installed, Vsn} |
%% {bad_relup_file, RelFile} |
%% {no_such_release, Vsn} |
%% {no_such_from_vsn, Vsn} |
+%% {could_not_create_hybrid_boot,Why} |
+%% {missing_base_app,Vsn,App} |
%% {illegal_option, Opt}} |
%% exit_reason()
%%-----------------------------------------------------------------
@@ -231,6 +244,21 @@ check_timeout(Int) when is_integer(Int), Int > 0 -> true;
check_timeout(_Else) -> false.
%%-----------------------------------------------------------------
+%% Purpose: Called by boot script after emulator is restarted due to
+%% new erts version.
+%% Returns: Same as install_release/2
+%% If this crashes, the emulator restart will fail
+%% (since the function is called from the boot script)
+%% and there will be a rollback.
+%%-----------------------------------------------------------------
+new_emulator_upgrade(Vsn, Opts) ->
+ Result = call({install_release, Vsn, reboot, Opts}),
+ error_logger:info_msg(
+ "~p:install_release(~p,~p) completed after node restart "
+ "with new emulator version~nResult: ~p~n",[?MODULE,Vsn,Opts,Result]),
+ Result.
+
+%%-----------------------------------------------------------------
%% Purpose: Makes the specified release version be the one that is
%% used when the system starts (or restarts).
%% The release must be installed (not unpacked).
@@ -322,7 +350,7 @@ check_script(Script, LibDirs) ->
%%-----------------------------------------------------------------
%% eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
%% {ok, UnPurged} |
-%% restart_new_emulator |
+%% restart_emulator |
%% {error, Error}
%% {'EXIT', Reason}
%% If sync_nodes is present, the calling process must have called
@@ -359,7 +387,7 @@ create_RELEASES(Root, RelDir, RelFile, LibDirs) ->
%%-----------------------------------------------------------------
%% Func: upgrade_app(App, Dir) -> {ok, Unpurged}
-%% | restart_new_emulator
+%% | restart_emulator
%% | {error, Error}
%% Types:
%% App = atom()
@@ -379,7 +407,7 @@ upgrade_app(App, NewDir) ->
%%-----------------------------------------------------------------
%% Func: downgrade_app(App, Dir)
%% downgrade_app(App, Vsn, Dir) -> {ok, Unpurged}
-%% | restart_new_emulator
+%% | restart_emulator
%% | {error, Error}
%% Types:
%% App = atom()
@@ -587,7 +615,7 @@ handle_call({check_install_release, Vsn, Purge}, _From, S) ->
handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) ->
NS = resend_sync_nodes(S),
case catch do_install_release(S, Vsn, Opts) of
- {ok, NewReleases, CurrentVsn, Descr} ->
+ {ok, NewReleases, [], CurrentVsn, Descr} ->
{reply, {ok, CurrentVsn, Descr}, NS#state{releases=NewReleases}};
{ok, NewReleases, Unpurged, CurrentVsn, Descr} ->
Timer =
@@ -602,10 +630,14 @@ handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) ->
{reply, {ok, CurrentVsn, Descr}, NewS};
{error, Reason} ->
{reply, {error, Reason}, NS};
- {restart_new_emulator, CurrentVsn, Descr} ->
+ {restart_emulator, CurrentVsn, Descr} ->
gen_server:reply(From, {ok, CurrentVsn, Descr}),
init:reboot(),
{noreply, NS};
+ {restart_new_emulator, CurrentVsn, Descr} ->
+ gen_server:reply(From, {continue_after_restart, CurrentVsn, Descr}),
+ init:reboot(),
+ {noreply, NS};
{'EXIT', Reason} ->
io:format("release_handler:"
"install_release(Vsn=~p Opts=~p) failed, "
@@ -950,7 +982,38 @@ do_install_release(#state{start_prg = StartPrg,
{value, Release} ->
LatestRelease = get_latest_release(Releases),
case get_rh_script(LatestRelease, Release, RelDir, Masters) of
+ {ok, {_CurrentVsn, _Descr, [restart_new_emulator|_Script]}}
+ when Static == true ->
+ throw(static_emulator);
+ {ok, {CurrentVsn, Descr, [restart_new_emulator|_Script]}} ->
+ %% This will only happen if the upgrade includes
+ %% an emulator upgrade (and it is not a downgrade)
+ %% - then the new emulator must be started before
+ %% new code can be loaded.
+ %% Create a temporary release which includes new
+ %% emulator, kernel, stdlib and sasl - and old
+ %% versions of other applications.
+ {TmpVsn,TmpRelease} =
+ new_emulator_make_tmp_release(LatestRelease,Release,
+ RelDir,Opts,Masters),
+ NReleases = [TmpRelease|Releases],
+
+ %% Then uppgrade to the temporary release.
+ %% The rest of the upgrade will continue after the restart
+ prepare_restart_new_emulator(StartPrg, RootDir,
+ RelDir, TmpVsn, TmpRelease,
+ NReleases, Masters),
+ {restart_new_emulator, CurrentVsn, Descr};
{ok, {CurrentVsn, Descr, Script}} ->
+ %% In case there has been an emulator upgrade,
+ %% remove the temporary release
+ NReleases =
+ new_emulator_rm_tmp_release(
+ LatestRelease#release.vsn,
+ LatestRelease#release.erts_vsn,
+ Vsn,RelDir,Releases,Masters),
+
+ %% Then execute the relup script
mon_nodes(true),
EnvBefore = application_controller:prep_config_change(),
Apps = change_appl_data(RelDir, Release, Masters),
@@ -958,31 +1021,19 @@ do_install_release(#state{start_prg = StartPrg,
NewLibs = get_new_libs(LatestRelease#release.libs,
Release#release.libs),
case eval_script(Script, Apps, LibDirs, NewLibs, Opts) of
- {ok, []} ->
- application_controller:config_change(EnvBefore),
- mon_nodes(false),
- NewReleases = set_status(Vsn, current, Releases),
- {ok, NewReleases, CurrentVsn, Descr};
{ok, Unpurged} ->
application_controller:config_change(EnvBefore),
mon_nodes(false),
- NewReleases = set_status(Vsn, current, Releases),
- {ok, NewReleases, Unpurged, CurrentVsn, Descr};
- restart_new_emulator when Static == true ->
+ NReleases1 = set_status(Vsn, current, NReleases),
+ {ok, NReleases1, Unpurged, CurrentVsn, Descr};
+ restart_emulator when Static == true ->
throw(static_emulator);
- restart_new_emulator ->
+ restart_emulator ->
mon_nodes(false),
- {value, PermanentRelease} =
- lists:keysearch(permanent, #release.status,
- Releases),
- NReleases = set_status(Vsn, current, Releases),
- NReleases2 = set_status(Vsn,tmp_current,NReleases),
- write_releases(RelDir, NReleases2, Masters),
prepare_restart_new_emulator(StartPrg, RootDir,
- RelDir, Release,
- PermanentRelease,
- Masters),
- {restart_new_emulator, CurrentVsn, Descr};
+ RelDir, Vsn, Release,
+ NReleases, Masters),
+ {restart_emulator, CurrentVsn, Descr};
Else ->
application_controller:config_change(EnvBefore),
mon_nodes(false),
@@ -995,6 +1046,145 @@ do_install_release(#state{start_prg = StartPrg,
{error, {no_such_release, Vsn}}
end.
+new_emulator_make_tmp_release(CurrentRelease,ToRelease,RelDir,Opts,Masters) ->
+ CurrentVsn = CurrentRelease#release.vsn,
+ ToVsn = ToRelease#release.vsn,
+ TmpVsn = ?tmp_vsn(CurrentVsn),
+ BaseApps = [kernel,stdlib,sasl],
+ BaseLibs = [{App,Vsn,Lib} || {App,Vsn,Lib} <- ToRelease#release.libs,
+ lists:member(App,BaseApps)],
+ check_base_libs(BaseLibs,ToVsn),
+ OldBaseLibs = [{App,Vsn,Lib} || {App,Vsn,Lib} <- CurrentRelease#release.libs,
+ lists:member(App,BaseApps)],
+ check_base_libs(OldBaseLibs,CurrentVsn),
+ RestLibs = [{App,Vsn,Lib} || {App,Vsn,Lib} <- CurrentRelease#release.libs,
+ not lists:member(App,BaseApps)],
+ TmpRelease = CurrentRelease#release{vsn=TmpVsn,
+ erts_vsn=ToRelease#release.erts_vsn,
+ libs = BaseLibs ++ RestLibs,
+ status = unpacked},
+ new_emulator_make_hybrid_boot(CurrentVsn,ToVsn,TmpVsn,BaseLibs,
+ RelDir,Opts,Masters),
+ new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters),
+ {TmpVsn,TmpRelease}.
+
+check_base_libs([_,_,_]=BaseLibs,_Vsn) ->
+ [Kernel,Sasl,Stdlib] = lists:keysort(1,BaseLibs),
+ [Kernel,Stdlib,Sasl];
+check_base_libs(SomeMissing,Vsn) ->
+ find_missing(SomeMissing,[kernel,stdlib,sasl],Vsn).
+
+find_missing(SomeMissing,[H|T],Vsn) ->
+ case lists:keymember(H,1,SomeMissing) of
+ true ->
+ find_missing(SomeMissing,T,Vsn);
+ false ->
+ throw({error,{missing_base_app,Vsn,H}})
+ end.
+
+new_emulator_make_hybrid_boot(CurrentVsn,ToVsn,TmpVsn,BaseLibs,RelDir,Opts,Masters) ->
+ FromBootFile = filename:join([RelDir,CurrentVsn,"start.boot"]),
+ ToBootFile = filename:join([RelDir,ToVsn,"start.boot"]),
+ TmpBootFile = filename:join([RelDir,TmpVsn,"start.boot"]),
+ ensure_dir(TmpBootFile,Masters),
+ Args = [ToVsn,Opts],
+ {ok,FromBoot} = read_file(FromBootFile,Masters),
+ {ok,ToBoot} = read_file(ToBootFile,Masters),
+ [KernelPath,SaslPath,StdlibPath] =
+ [filename:join(Path,ebin) || {_,_,Path} <- lists:keysort(1,BaseLibs)],
+ Paths = {KernelPath,StdlibPath,SaslPath},
+ case systools_make:make_hybrid_boot(TmpVsn,FromBoot,ToBoot,Paths,Args) of
+ {ok,TmpBoot} ->
+ write_file(TmpBootFile,TmpBoot,Masters);
+ {error,Reason} ->
+ throw({error,{could_not_create_hybrid_boot,Reason}})
+ end.
+
+new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) ->
+ FromFile = filename:join([RelDir,CurrentVsn,"sys.config"]),
+ ToFile = filename:join([RelDir,ToVsn,"sys.config"]),
+ TmpFile = filename:join([RelDir,TmpVsn,"sys.config"]),
+
+ FromConfig =
+ case consult(FromFile,Masters) of
+ {ok,[FC]} ->
+ FC;
+ {error,Error1} ->
+ io:format("Warning: ~p can not read ~p: ~p~n",
+ [?MODULE,FromFile,Error1]),
+ []
+ end,
+
+ [Kernel,Stdlib,Sasl] =
+ case consult(ToFile,Masters) of
+ {ok,[ToConfig]} ->
+ [lists:keyfind(App,1,ToConfig) || App <- [kernel,stdlib,sasl]];
+ {error,Error2} ->
+ io:format("Warning: ~p can not read ~p: ~p~n",
+ [?MODULE,ToFile,Error2]),
+ [false,false,false]
+ end,
+
+ Config1 = replace_config(kernel,FromConfig,Kernel),
+ Config2 = replace_config(stdlib,Config1,Stdlib),
+ Config3 = replace_config(sasl,Config2,Sasl),
+
+ ConfigStr = io_lib:format("~p.~n",[Config3]),
+ write_file(TmpFile,ConfigStr,Masters).
+
+%% Take the configuration for application App from the new config and
+%% insert in the old config.
+%% If no entry exists in the new config, then delete the entry (if it exists)
+%% from the old config.
+%% If entry exists in the new config, but not in the old config, then
+%% add the entry.
+replace_config(App,Config,false) ->
+ lists:keydelete(App,1,Config);
+replace_config(App,Config,AppConfig) ->
+ lists:keystore(App,1,Config,AppConfig).
+
+%% Remove all files related to the temporary release
+new_emulator_rm_tmp_release(?tmp_vsn(_)=TmpVsn,EVsn,NewVsn,
+ RelDir,Releases,Masters) ->
+ case os:type() of
+ {win32, nt} ->
+ rename_tmp_service(EVsn,TmpVsn,NewVsn);
+ _ ->
+ ok
+ end,
+ remove_dir(filename:join(RelDir,TmpVsn),Masters),
+ lists:keydelete(TmpVsn,#release.vsn,Releases);
+new_emulator_rm_tmp_release(_,_,_,_,Releases,_) ->
+ Releases.
+
+%% Rename the tempoarary service (for erts ugprade) to the real ToVsn
+rename_tmp_service(EVsn,TmpVsn,NewVsn) ->
+ FromName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ TmpVsn,
+ ToName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ NewVsn,
+ case erlsrv:get_service(EVsn,ToName) of
+ {error, _Error} ->
+ ok;
+ _Data ->
+ erlsrv:remove_service(ToName)
+ end,
+ rename_service(EVsn,FromName,ToName).
+
+
+%% Rename a service and check that it succeeded
+rename_service(EVsn,FromName,ToName) ->
+ case erlsrv:rename_service(EVsn,FromName,ToName) of
+ {ok,_} ->
+ case erlsrv:get_service(EVsn,ToName) of
+ {error,Error1} ->
+ throw({error,Error1});
+ _Data2 ->
+ ok
+ end;
+ Error2 ->
+ throw({error,{service_rename_failed, Error2}})
+ end.
+
+
%%% This code chunk updates the services in one of two ways,
%%% Either the emulator is restarted, in which case the old service
%%% is to be removed and the new enabled, or the emulator is NOT restarted
@@ -1011,26 +1201,16 @@ do_make_services_permanent(PermanentVsn,Vsn, PermanentEVsn, EVsn) ->
%% rename.
case os:getenv("ERLSRV_SERVICE_NAME") == PermName of
true ->
- case erlsrv:rename_service(EVsn,PermName,Name) of
- {ok,_} ->
- case erlsrv:get_service(EVsn,Name) of
- {error,Error2} ->
- throw({error,Error2});
- _Data2 ->
- %% The interfaces for doing this are
- %% NOT published and may be subject to
- %% change. Do NOT do this anywhere else!
-
- os:putenv("ERLSRV_SERVICE_NAME", Name),
-
- %% Restart heart port program, this
- %% function is only to be used here.
- heart:cycle(),
- ok
- end;
- Error3 ->
- throw({error,{service_rename_failed, Error3}})
- end;
+ rename_service(EVsn,PermName,Name),
+ %% The interfaces for doing this are
+ %% NOT published and may be subject to
+ %% change. Do NOT do this anywhere else!
+
+ os:putenv("ERLSRV_SERVICE_NAME", Name),
+
+ %% Restart heart port program, this
+ %% function is only to be used here.
+ heart:cycle();
false ->
throw({error,service_name_missmatch})
end;
@@ -1269,21 +1449,31 @@ do_set_removed(RelDir, Vsn, Releases, Masters) ->
%% corresponding relup instructions, we check if it's possible to
%% downgrade from CurrentVsn to ToVsn.
%%-----------------------------------------------------------------
+get_rh_script(#release{vsn = ?tmp_vsn(CurrentVsn)},
+ #release{vsn = ToVsn},
+ RelDir,
+ Masters) ->
+ {ok,{Vsn,Descr,[restart_new_emulator|Script]}} =
+ do_get_rh_script(CurrentVsn,ToVsn,RelDir,Masters),
+ {ok,{Vsn,Descr,Script}};
get_rh_script(#release{vsn = CurrentVsn},
- #release{vsn = Vsn},
+ #release{vsn = ToVsn},
RelDir,
Masters) ->
- Relup = filename:join([RelDir, Vsn, "relup"]),
- case try_upgrade(Vsn, CurrentVsn, Relup, Masters) of
+ do_get_rh_script(CurrentVsn,ToVsn,RelDir,Masters).
+
+do_get_rh_script(CurrentVsn, ToVsn, RelDir, Masters) ->
+ Relup = filename:join([RelDir, ToVsn, "relup"]),
+ case try_upgrade(ToVsn, CurrentVsn, Relup, Masters) of
{ok, RhScript} ->
{ok, RhScript};
_ ->
Relup2 = filename:join([RelDir, CurrentVsn,"relup"]),
- case try_downgrade(Vsn, CurrentVsn, Relup2, Masters) of
+ case try_downgrade(ToVsn, CurrentVsn, Relup2, Masters) of
{ok, RhScript} ->
{ok, RhScript};
_ ->
- throw({error, {no_matching_relup, Vsn, CurrentVsn}})
+ throw({error, {no_matching_relup, ToVsn, CurrentVsn}})
end
end.
@@ -1496,6 +1686,15 @@ prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn},
%% restart is performed by calling init:reboot() higher up.
%%-----------------------------------------------------------------
prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
+ Vsn, Release, Releases, Masters) ->
+ {value, PRelease} = lists:keysearch(permanent, #release.status,Releases),
+ NReleases1 = set_status(Vsn, current, Releases),
+ NReleases2 = set_status(Vsn,tmp_current,NReleases1),
+ write_releases(RelDir, NReleases2, Masters),
+ prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
+ Release, PRelease, Masters).
+
+prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
Release, PRelease, Masters) ->
#release{erts_vsn = EVsn, vsn = Vsn} = Release,
Data = EVsn ++ " " ++ Vsn,
@@ -1521,19 +1720,10 @@ check_start_prg({do_check, StartPrg}, Masters) ->
check_start_prg({_, StartPrg}, _) ->
StartPrg.
-write_new_start_erl(Data, RelDir, false) ->
- DataFile = filename:join([RelDir, "new_start_erl.data"]),
- case do_write_file(DataFile, Data) of
- ok -> DataFile;
- Error -> throw(Error)
- end;
write_new_start_erl(Data, RelDir, Masters) ->
DataFile = filename:join([RelDir, "new_start_erl.data"]),
- case at_all_masters(Masters, ?MODULE, do_write_file,
- [DataFile, Data]) of
- ok -> DataFile;
- Error -> throw(Error)
- end.
+ write_file(DataFile, Data, Masters),
+ DataFile.
%%-----------------------------------------------------------------
%% When a new emulator shall be restarted, the current release
@@ -1547,27 +1737,41 @@ write_new_start_erl(Data, RelDir, Masters) ->
%% If the release is made permanent, this is written to disk.
%%-----------------------------------------------------------------
transform_release(ReleaseDir, Releases, Masters) ->
- F = fun(Release) when Release#release.status == tmp_current ->
- Release#release{status = unpacked};
- (Release) -> Release
- end,
- case lists:map(F, Releases) of
- Releases ->
- Releases;
- DReleases ->
+ case init:script_id() of
+ {Name, ?tmp_vsn(_)=TmpVsn} ->
+ %% This is was a reboot due to a new emulator version. The
+ %% current release is a temporary internal release, which
+ %% must be removed. It is the "real new release" that is
+ %% set to unpacked on disk and current in memory.
+ DReleases = lists:keydelete(TmpVsn,#release.vsn,Releases),
write_releases(ReleaseDir, DReleases, Masters),
- F1 = fun(Release) when Release#release.status == tmp_current ->
- case init:script_id() of
- {_Name, Vsn} when Release#release.vsn == Vsn ->
- Release#release{status = current};
- _ ->
- Release#release{status = unpacked}
- end;
- (Release) -> Release
- end,
- lists:map(F1, Releases)
+ set_current({Name,TmpVsn},Releases);
+ ScriptId ->
+ F = fun(Release) when Release#release.status == tmp_current ->
+ Release#release{status = unpacked};
+ (Release) -> Release
+ end,
+ case lists:map(F, Releases) of
+ Releases ->
+ Releases;
+ DReleases ->
+ write_releases(ReleaseDir, DReleases, Masters),
+ set_current(ScriptId, Releases)
+ end
end.
+set_current(ScriptId, Releases) ->
+ F1 = fun(Release) when Release#release.status == tmp_current ->
+ case ScriptId of
+ {_Name,Vsn} when Release#release.vsn == Vsn ->
+ Release#release{status = current};
+ _ ->
+ Release#release{status = unpacked}
+ end;
+ (Release) -> Release
+ end,
+ lists:map(F1, Releases).
+
%%-----------------------------------------------------------------
%% Functions handling files, RELEASES, start_erl.data etc.
%% This functions consider if the release_handler is a client and
@@ -1626,12 +1830,25 @@ extract_tar(Root, Tar) ->
throw({error, {cannot_extract_file, Name, Reason}})
end.
-write_releases(Dir, NewReleases, false) ->
+write_releases(Dir, Releases, Masters) ->
+ %% We must never write 'current' to disk, since this will confuse
+ %% us after a node restart - since we would then have a permanent
+ %% release running, but state set to current for a non-running
+ %% release.
+ NewReleases = lists:zf(fun(Release) when Release#release.status == current ->
+ {true, Release#release{status = unpacked}};
+ (_) ->
+ true
+ end, Releases),
+ write_releases_1(Dir, NewReleases, Masters).
+
+
+write_releases_1(Dir, NewReleases, false) ->
case do_write_release(Dir, "RELEASES", NewReleases) of
ok -> ok;
Error -> throw(Error)
end;
-write_releases(Dir, NewReleases, Masters) ->
+write_releases_1(Dir, NewReleases, Masters) ->
all_masters(Masters),
write_releases_m(Dir, NewReleases, Masters).
@@ -1853,6 +2070,37 @@ read_file(File, false) ->
read_file(File, Masters) ->
read_master(Masters, File).
+write_file(File, Data, false) ->
+ case file:write_file(File, Data) of
+ ok -> ok;
+ Error -> throw(Error)
+ end;
+write_file(File, Data, Masters) ->
+ case at_all_masters(Masters, file, write_file, [File, Data]) of
+ ok -> ok;
+ Error -> throw(Error)
+ end.
+
+ensure_dir(File, false) ->
+ case filelib:ensure_dir(File) of
+ ok -> ok;
+ Error -> throw(Error)
+ end;
+ensure_dir(File, Masters) ->
+ case at_all_masters(Masters,filelib,ensure_dir,[File]) of
+ ok -> ok;
+ Error -> throw(Error)
+ end.
+
+remove_dir(Dir, false) ->
+ remove_file(Dir);
+remove_dir(Dir, Masters) ->
+ case at_all_masters(Masters,?MODULE,remove_file,[Dir]) of
+ ok -> ok;
+ Error -> throw(Error)
+ end.
+
+
%% Ignore status of each delete !
remove_files(Master, Files, Masters) ->
takewhile(Master, Masters, ?MODULE, do_remove_files, [Files]).
diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl
index 8d0baf3ab1..b4b288646f 100644
--- a/lib/sasl/src/release_handler_1.erl
+++ b/lib/sasl/src/release_handler_1.erl
@@ -47,26 +47,38 @@
%%%-----------------------------------------------------------------
%%% This is a low-level release handler.
%%%-----------------------------------------------------------------
+check_script([restart_new_emulator|Script], LibDirs) ->
+ %% There is no need to check for old processes, since the node
+ %% will be restarted before anything else happens.
+ do_check_script(Script, LibDirs, []);
check_script(Script, LibDirs) ->
case catch check_old_processes(Script,soft_purge) of
{ok, PurgeMods} ->
- {Before, _After} = split_instructions(Script),
- case catch lists:foldl(fun(Instruction, EvalState1) ->
- eval(Instruction, EvalState1)
- end,
- #eval_state{libdirs = LibDirs},
- Before) of
- EvalState2 when is_record(EvalState2, eval_state) ->
- {ok,PurgeMods};
- {error, Error} ->
- {error, Error};
- Other ->
- {error, Other}
- end;
+ do_check_script(Script, LibDirs, PurgeMods);
{error, Mod} ->
{error, {old_processes, Mod}}
end.
+do_check_script(Script, LibDirs, PurgeMods) ->
+ {Before, After} = split_instructions(Script),
+ case catch lists:foldl(fun(Instruction, EvalState1) ->
+ eval(Instruction, EvalState1)
+ end,
+ #eval_state{libdirs = LibDirs},
+ Before) of
+ EvalState2 when is_record(EvalState2, eval_state) ->
+ case catch syntax_check_script(After) of
+ ok ->
+ {ok,PurgeMods};
+ Other ->
+ {error,Other}
+ end;
+ {error, Error} ->
+ {error, Error};
+ Other ->
+ {error, Other}
+ end.
+
%% eval_script/1 - For testing only - no apps added, just testing instructions
eval_script(Script) ->
eval_script(Script, [], [], [], []).
@@ -83,22 +95,30 @@ eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
newlibs = NewLibs,
opts = Opts},
Before) of
- EvalState2 when is_record(EvalState2, eval_state) ->
- case catch lists:foldl(fun(Instruction, EvalState3) ->
- eval(Instruction, EvalState3)
- end,
- EvalState2,
- After) of
- EvalState4 when is_record(EvalState4, eval_state) ->
- {ok, EvalState4#eval_state.unpurged};
- restart_new_emulator ->
- restart_new_emulator;
- Error ->
- {'EXIT', Error}
- end;
- {error, Error} -> {error, Error};
- Other -> {error, Other}
- end;
+ EvalState2 when is_record(EvalState2, eval_state) ->
+ case catch syntax_check_script(After) of
+ ok ->
+ case catch lists:foldl(
+ fun(Instruction, EvalState3) ->
+ eval(Instruction,
+ EvalState3)
+ end,
+ EvalState2,
+ After) of
+ EvalState4
+ when is_record(EvalState4, eval_state) ->
+ {ok, EvalState4#eval_state.unpurged};
+ restart_emulator ->
+ restart_emulator;
+ Error ->
+ {'EXIT', Error}
+ end;
+ Other ->
+ {error,Other}
+ end;
+ {error, Error} -> {error, Error};
+ Other -> {error, Other}
+ end;
{error, Mod} ->
{error, {old_processes, Mod}}
end.
@@ -174,6 +194,42 @@ do_check_old_code(Mod,Procs) ->
[Mod].
+%% Check the last part of the script, i.e. the part after point_of_no_return.
+%% More or less a syntax check in case the script was handwritten.
+syntax_check_script([point_of_no_return | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{load, {_,_,_}} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{remove, {_,_,_}} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{purge, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{suspend, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{resume, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{code_change, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{code_change, _, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{stop, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{start, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{sync_nodes, _, {_,_,_}} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{sync_nodes, _, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{apply, {_,_,_}} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([restart_emulator | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([Illegal | _Script]) ->
+ throw({illegal_instruction_after_point_of_no_return,Illegal});
+syntax_check_script([]) ->
+ ok.
+
+
%%-----------------------------------------------------------------
%% An unpurged module is a module for which there exist an old
%% version of the code. This should only be the case if there are
@@ -243,7 +299,7 @@ do_check_old_code(Mod,Procs) ->
%% must also exectue the same line. Waits for all these nodes to get
%% to this line.
%% point_of_no_return
-%% restart_new_emulator
+%% restart_emulator
%% {stop_application, Appl} - Impl with apply
%% {unload_application, Appl} - Impl with {remove..}
%% {load_application, Appl} - Impl with {load..}
@@ -402,8 +458,8 @@ eval({sync_nodes, Id, Nodes}, EvalState) ->
eval({apply, {M, F, A}}, EvalState) ->
apply(M, F, A),
EvalState;
-eval(restart_new_emulator, _EvalState) ->
- throw(restart_new_emulator).
+eval(restart_emulator, _EvalState) ->
+ throw(restart_emulator).
get_opt(Tag, EvalState, Default) ->
case lists:keysearch(Tag, 1, EvalState#eval_state.opts) of
diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src
index 64c653a4e5..ce4aa1f8f8 100644
--- a/lib/sasl/src/sasl.appup.src
+++ b/lib/sasl/src/sasl.appup.src
@@ -1,25 +1,27 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
-%% %CopyrightEnd%
%%
-
+%% %CopyrightEnd%
{"%VSN%",
- [{"2.1.4", [{load_module, release_handler},
- {load_module, systools_relup}]}],
- [{"2.1.4", [{load_module, release_handler},
- {load_module, systools_relup}]}]
+ %% Up from - max two major revisions back
+ [{<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.1\\.10(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14B04 (and later?)
+ {<<"2\\.1\\.[6-9](\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13B-R14B03
+ %% Down to - max two major revisions back
+ [{<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.1\\.10(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14B04 (and later?)
+ {<<"2\\.1\\.[6-9](\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13B-R14B03
}.
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 7f400f5cce..ce37f3c2ce 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -31,6 +31,8 @@
-export([read_application/4]).
+-export([make_hybrid_boot/5]).
+
-import(lists, [filter/2, keysort/2, keysearch/3, map/2, reverse/1,
append/1, foldl/3, member/2, foreach/2]).
@@ -162,6 +164,118 @@ return({error,Mod,Error},_,Flags) ->
error
end.
+
+%%-----------------------------------------------------------------
+%% Make hybrid boot file for upgrading emulator. The resulting boot
+%% file is a combination of the two input files, where kernel, stdlib
+%% and sasl versions are taken from the second file (the boot file of
+%% the new release), and all other application versions from the first
+%% file (the boot file of the old release).
+%%
+%% The most important thing that can fail here is that the input boot
+%% files do not contain all three base applications - kernel, stdlib
+%% and sasl.
+%%
+%% TmpVsn = string(),
+%% Paths = {KernelPath,StdlibPath,SaslPath}
+%% Returns {ok,Boot} | {error,Reason}
+%% Boot1 = Boot2 = Boot = binary()
+%% Reason = {app_not_found,App} | {app_not_replaced,App}
+%% App = kernel | stdlib | sasl
+make_hybrid_boot(TmpVsn, Boot1, Boot2, Paths, Args) ->
+ catch do_make_hybrid_boot(TmpVsn, Boot1, Boot2, Paths, Args).
+do_make_hybrid_boot(TmpVsn, Boot1, Boot2, Paths, Args) ->
+ {script,{_RelName1,_RelVsn1},Script1} = binary_to_term(Boot1),
+ {script,{RelName2,_RelVsn2},Script2} = binary_to_term(Boot2),
+ MatchPaths = get_regexp_path(Paths),
+ NewScript1 = replace_paths(Script1,MatchPaths),
+ {Kernel,Stdlib,Sasl} = get_apps(Script2,undefined,undefined,undefined),
+ NewScript2 = replace_apps(NewScript1,Kernel,Stdlib,Sasl),
+ NewScript3 = add_apply_upgrade(NewScript2,Args),
+ Boot = term_to_binary({script,{RelName2,TmpVsn},NewScript3}),
+ {ok,Boot}.
+
+%% For each app, compile a regexp that can be used for finding its path
+get_regexp_path({KernelPath,StdlibPath,SaslPath}) ->
+ {ok,KernelMP} = re:compile("kernel-[0-9\.]+",[unicode]),
+ {ok,StdlibMP} = re:compile("stdlib-[0-9\.]+",[unicode]),
+ {ok,SaslMP} = re:compile("sasl-[0-9\.]+",[unicode]),
+ [{KernelMP,KernelPath},{StdlibMP,StdlibPath},{SaslMP,SaslPath}].
+
+%% For each path in the script, check if it matches any of the MPs
+%% found above, and if so replace it with the correct new path.
+replace_paths([{path,Path}|Script],MatchPaths) ->
+ [{path,replace_path(Path,MatchPaths)}|replace_paths(Script,MatchPaths)];
+replace_paths([Stuff|Script],MatchPaths) ->
+ [Stuff|replace_paths(Script,MatchPaths)];
+replace_paths([],_) ->
+ [].
+
+replace_path([Path|Paths],MatchPaths) ->
+ [do_replace_path(Path,MatchPaths)|replace_path(Paths,MatchPaths)];
+replace_path([],_) ->
+ [].
+
+do_replace_path(Path,[{MP,ReplacePath}|MatchPaths]) ->
+ case re:run(Path,MP,[{capture,none}]) of
+ nomatch -> do_replace_path(Path,MatchPaths);
+ match -> ReplacePath
+ end;
+do_replace_path(Path,[]) ->
+ Path.
+
+%% Return the entries for loading the three base applications
+get_apps([{kernelProcess,application_controller,
+ {application_controller,start,[{application,kernel,_}]}}=Kernel|
+ Script],_,Stdlib,Sasl) ->
+ get_apps(Script,Kernel,Stdlib,Sasl);
+get_apps([{apply,{application,load,[{application,stdlib,_}]}}=Stdlib|Script],
+ Kernel,_,Sasl) ->
+ get_apps(Script,Kernel,Stdlib,Sasl);
+get_apps([{apply,{application,load,[{application,sasl,_}]}}=Sasl|_Script],
+ Kernel,Stdlib,_) ->
+ {Kernel,Stdlib,Sasl};
+get_apps([_|Script],Kernel,Stdlib,Sasl) ->
+ get_apps(Script,Kernel,Stdlib,Sasl);
+get_apps([],undefined,_,_) ->
+ throw({error,{app_not_found,kernel}});
+get_apps([],_,undefined,_) ->
+ throw({error,{app_not_found,stdlib}});
+get_apps([],_,_,undefined) ->
+ throw({error,{app_not_found,sasl}}).
+
+
+%% Replace the entries for loading the base applications
+replace_apps([{kernelProcess,application_controller,
+ {application_controller,start,[{application,kernel,_}]}}|
+ Script],Kernel,Stdlib,Sasl) ->
+ [Kernel|replace_apps(Script,undefined,Stdlib,Sasl)];
+replace_apps([{apply,{application,load,[{application,stdlib,_}]}}|Script],
+ Kernel,Stdlib,Sasl) ->
+ [Stdlib|replace_apps(Script,Kernel,undefined,Sasl)];
+replace_apps([{apply,{application,load,[{application,sasl,_}]}}|Script],
+ _Kernel,_Stdlib,Sasl) ->
+ [Sasl|Script];
+replace_apps([Stuff|Script],Kernel,Stdlib,Sasl) ->
+ [Stuff|replace_apps(Script,Kernel,Stdlib,Sasl)];
+replace_apps([],undefined,undefined,_) ->
+ throw({error,{app_not_replaced,sasl}});
+replace_apps([],undefined,_,_) ->
+ throw({error,{app_not_replaced,stdlib}});
+replace_apps([],_,_,_) ->
+ throw({error,{app_not_replaced,kernel}}).
+
+
+%% Finally add an apply of release_handler:new_emulator_upgrade - which will
+%% complete the execution of the upgrade script (relup).
+add_apply_upgrade(Script,Args) ->
+ [{progress, started} | RevScript] = lists:reverse(Script),
+ lists:reverse([{progress,started},
+ {apply,{release_handler,new_emulator_upgrade,Args}} |
+ RevScript]).
+
+
+
%%-----------------------------------------------------------------
%% Create a release package from a release file.
%% Options is a list of {path, Path} | silent |
@@ -250,13 +364,13 @@ get_release(File, Path, ModTestP, Machine) ->
end.
get_release1(File, Path, ModTestP, Machine) ->
- {ok, Release} = read_release(File, Path),
+ {ok, Release, Warnings1} = read_release(File, Path),
{ok, Appls0} = collect_applications(Release, Path),
{ok, Appls1} = check_applications(Appls0),
{ok, Appls2} = sort_included_applications(Appls1, Release), % OTP-4121
- {ok, Warnings} = check_modules(Appls2, Path, ModTestP, Machine),
+ {ok, Warnings2} = check_modules(Appls2, Path, ModTestP, Machine),
{ok, Appls} = sort_appls(Appls2),
- {ok, Release, Appls, Warnings}.
+ {ok, Release, Appls, Warnings1 ++ Warnings2}.
%%______________________________________________________________________
%% read_release(File, Path) -> {ok, #release} | throw({error, What})
@@ -271,11 +385,12 @@ read_release(File, Path) ->
check_rel(Release) ->
case catch check_rel1(Release) of
- {ok, {Name,Vsn,Evsn,Appl,Incl}} ->
+ {ok, {Name,Vsn,Evsn,Appl,Incl}, Ws} ->
{ok, #release{name=Name, vsn=Vsn,
erts_vsn=Evsn,
applications=Appl,
- incl_apps=Incl}};
+ incl_apps=Incl},
+ Ws};
{error, Error} ->
throw({error,?MODULE,Error});
Error ->
@@ -286,8 +401,8 @@ check_rel1({release,{Name,Vsn},{erts,EVsn},Appl}) when is_list(Appl) ->
check_name(Name),
check_vsn(Vsn),
check_evsn(EVsn),
- {Appls,Incls} = check_appl(Appl),
- {ok, {Name,Vsn,EVsn,Appls,Incls}};
+ {{Appls,Incls},Ws} = check_appl(Appl),
+ {ok, {Name,Vsn,EVsn,Appls,Incls},Ws};
check_rel1(_) ->
{error, badly_formatted_release}.
@@ -340,8 +455,8 @@ check_appl(Appl) ->
end,
Appl) of
[] ->
- mandatory_applications(Appl),
- split_app_incl(Appl);
+ {ok,Ws} = mandatory_applications(Appl),
+ {split_app_incl(Appl),Ws};
Illegal ->
throw({error, {illegal_applications,Illegal}})
end.
@@ -352,7 +467,12 @@ mandatory_applications(Appl) ->
Mand = mandatory_applications(),
case filter(fun(X) -> member(X, AppNames) end, Mand) of
Mand ->
- ok;
+ case member(sasl,AppNames) of
+ true ->
+ {ok,[]};
+ _ ->
+ {ok, [{warning,missing_sasl}]}
+ end;
_ ->
throw({error, {missing_mandatory_app, Mand}})
end.
@@ -2198,5 +2318,9 @@ form_warn(Prefix, {exref_undef, Undef}) ->
[Prefix,M,F,A])
end,
map(F, Undef);
+form_warn(Prefix, missing_sasl) ->
+ io_lib:format("~s: Missing application sasl. "
+ "Can not upgrade with this release~n",
+ [Prefix]);
form_warn(Prefix, What) ->
io_lib:format("~s ~p~n", [Prefix,What]).
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
index daadb79967..c16f6aa845 100644
--- a/lib/sasl/src/systools_rc.erl
+++ b/lib/sasl/src/systools_rc.erl
@@ -54,6 +54,7 @@
%% {sync_nodes, Id, Nodes}
%% {apply, {M, F, A}}
%% restart_new_emulator
+%% restart_emulator
%%-----------------------------------------------------------------
%% High-level instructions that contain dependencies
@@ -144,7 +145,10 @@ translate_merged_script(Mode, Script, Appls, PreAppls) ->
{Before2, After2} = translate_dependent_instrs(Mode, Before1, After1,
Appls),
Before3 = merge_load_object_code(Before2),
- NewScript = Before3 ++ [point_of_no_return | After2],
+
+ {Before4,After4} = sort_emulator_restart(Mode,Before3,After2),
+ NewScript = Before4 ++ [point_of_no_return | After4],
+
check_syntax(NewScript),
{ok, NewScript}.
@@ -699,6 +703,39 @@ mlo([{load_object_code, {Lib, LibVsn, Mods}} | T]) ->
mlo([]) -> [].
%%-----------------------------------------------------------------
+%% RESTART EMULATOR
+%% -----------------------------------------------------------------
+%% -----------------------------------------------------------------
+%% Check if there are any 'restart_new_emulator' instructions (i.e. if
+%% the emulator or core application version is changed). If so, this
+%% must be done first for upgrade and last for downgrade.
+%% Check if there are any 'restart_emulator' instructions, if so
+%% remove all and place one the end.
+%% -----------------------------------------------------------------
+sort_emulator_restart(Mode,Before,After) ->
+ {Before1,After1} =
+ case filter_out(restart_new_emulator, After) of
+ After ->
+ {Before,After};
+ A1 when Mode==up ->
+ {[restart_new_emulator|Before],A1};
+ A1 when Mode==dn ->
+ {Before,A1++[restart_emulator]}
+ end,
+ After2 =
+ case filter_out(restart_emulator, After1) of
+ After1 ->
+ After1;
+ A2 ->
+ A2++[restart_emulator]
+ end,
+ {Before1,After2}.
+
+
+filter_out(What,List) ->
+ lists:filter(fun(X) when X=:=What -> false; (_) -> true end, List).
+
+%%-----------------------------------------------------------------
%% SYNTAX CHECK
%%-----------------------------------------------------------------
%%-----------------------------------------------------------------
@@ -817,6 +854,7 @@ check_op({apply, {M, F, A}}) ->
check_func(F),
check_args(A);
check_op(restart_new_emulator) -> ok;
+check_op(restart_emulator) -> ok;
check_op(X) -> throw({error, {bad_instruction, X}}).
check_mod(Mod) when is_atom(Mod) -> ok;
diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl
index 6d9e922900..7fb623bb85 100644
--- a/lib/sasl/src/systools_relup.erl
+++ b/lib/sasl/src/systools_relup.erl
@@ -112,6 +112,11 @@
-export([mk_relup/3, mk_relup/4, format_error/1, format_warning/1]).
-include("systools.hrl").
+-define(R15_SASL_VSN,"2.2").
+
+%% For test purposes only - used by kernel, stdlib and sasl tests
+-export([appup_search_for_version/2]).
+
%%-----------------------------------------------------------------
%% mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs)
%% mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Opts) -> Ret
@@ -200,7 +205,13 @@ do_mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Path, Opts) ->
%% TopRel = #release
%% NameVsnApps = [{{Name, Vsn}, #application}]
{ok, TopRel, NameVsnApps, Ws0} ->
- %%
+ case lists:member({warning,missing_sasl},Ws0) of
+ true ->
+ throw({error,?MODULE,{missing_sasl,TopRel}});
+ false ->
+ ok
+ end,
+
%% TopApps = [#application]
TopApps = lists:map(fun({_, App}) -> App end, NameVsnApps),
@@ -247,7 +258,21 @@ foreach_baserel_up(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
Ws, Acc) ->
BaseRelFile = extract_filename(BaseRelDc),
- {ok, BaseRel} = systools_make:read_release(BaseRelFile, Path),
+ {BaseRel, {BaseNameVsns, BaseApps}, Ws0} =
+ case systools_make:get_release(BaseRelFile, Path) of
+ {ok, BR, NameVsnApps, Warns} ->
+ case lists:member({warning,missing_sasl},Warns) of
+ true ->
+ throw({error,?MODULE,{missing_sasl,BR}});
+ false ->
+ %% NameVsnApps = [{{Name,Vsn},#application}]
+ %% Gives two lists - [{Name,Vsn}] and [#application]
+ {BR,lists:unzip(NameVsnApps),Warns}
+ end;
+ Other1 ->
+ throw(Other1)
+ end,
+
%%
%% BaseRel = #release
@@ -257,29 +282,23 @@ foreach_baserel_up(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
%% application, one for each added applications, and one for
%% each removed applications.
%%
- {RUs1, Ws1} = collect_appup_scripts(up, TopApps, BaseRel, Ws, []),
+ {RUs1, Ws1} = collect_appup_scripts(up, TopApps, BaseRel, Ws0++Ws, []),
{RUs2, Ws2} = create_add_app_scripts(BaseRel, TopRel, RUs1, Ws1),
{RUs3, Ws3} = create_remove_app_scripts(BaseRel, TopRel, RUs2, Ws2),
- {RUs4, Ws4} =
- check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts),
-
- BaseApps =
- case systools_make:get_release(BaseRelFile, Path) of
- {ok, _, NameVsnApps, _Warns} ->
- lists:map(fun({_,App}) -> App end, NameVsnApps);
- Other1 ->
- throw(Other1)
- end,
+ {RUs4, Ws4} = check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts),
case systools_rc:translate_scripts(up, RUs4, TopApps, BaseApps) of
- {ok, RUs} ->
+ {ok, RUs5} ->
+
+ {RUs, Ws5} = fix_r15_sasl_upgrade(RUs5,Ws4,BaseNameVsns),
+
VDR = {BaseRel#release.vsn,
extract_description(BaseRelDc), RUs},
foreach_baserel_up(TopRel, TopApps, BaseRelDcs, Path,
- Opts, Ws4, [VDR| Acc]);
+ Opts, Ws5, [VDR| Acc]);
XXX ->
throw(XXX)
end;
@@ -294,42 +313,41 @@ foreach_baserel_dn(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
Ws, Acc) ->
BaseRelFile = extract_filename(BaseRelDc),
- {ok, BaseRel} = systools_make:read_release(BaseRelFile, Path),
-
- %% BaseRel = #release
-
- %% RUs = (release upgrade scripts)
- %%
- {RUs1, Ws1} = collect_appup_scripts(dn, TopApps, BaseRel, Ws, []),
-
- {BaseApps, Ws2} =
+ {BaseRel, BaseApps, Ws0} =
case systools_make:get_release(BaseRelFile, Path) of
%%
%% NameVsnApps = [{{Name, Vsn}, #application}]
- {ok, _, NameVsnApps, Warns} ->
- %%
- %% NApps = [#application]
- NApps = lists:map(fun({_,App}) -> App end, NameVsnApps),
- {NApps, Warns ++ Ws1};
+ {ok, BR, NameVsnApps, Warns} ->
+ case lists:member({warning,missing_sasl},Warns) of
+ true ->
+ throw({error,?MODULE,{missing_sasl,BR}});
+ false ->
+ %% NApps = [#application]
+ NApps = lists:map(fun({_,App}) -> App end, NameVsnApps),
+ {BR, NApps, Warns}
+ end;
Other ->
throw(Other)
end,
- RUs2 = RUs1,
+ %% BaseRel = #release
+
+ %% RUs = (release upgrade scripts)
+ %%
+ {RUs1, Ws1} = collect_appup_scripts(dn, TopApps, BaseRel, Ws0++Ws, []),
- {RUs3, Ws3} = create_add_app_scripts(TopRel, BaseRel, RUs2, Ws2),
+ {RUs2, Ws2} = create_add_app_scripts(TopRel, BaseRel, RUs1, Ws1),
- {RUs4, Ws4} = create_remove_app_scripts(TopRel, BaseRel, RUs3, Ws3),
+ {RUs3, Ws3} = create_remove_app_scripts(TopRel, BaseRel, RUs2, Ws2),
- {RUs5, Ws5} = check_for_emulator_restart(TopRel, BaseRel,
- RUs4, Ws4, Opts),
+ {RUs4, Ws4} = check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts),
- case systools_rc:translate_scripts(dn, RUs5, BaseApps, TopApps) of
+ case systools_rc:translate_scripts(dn, RUs4, BaseApps, TopApps) of
{ok, RUs} ->
VDR = {BaseRel#release.vsn,
extract_description(BaseRelDc), RUs},
foreach_baserel_dn(TopRel, TopApps, BaseRelDcs, Path,
- Opts, Ws5, [VDR| Acc]);
+ Opts, Ws4, [VDR| Acc]);
XXX ->
throw(XXX)
end;
@@ -343,14 +361,42 @@ foreach_baserel_dn( _, _, [], _, _, Ws, Acc) ->
%%
check_for_emulator_restart(#release{erts_vsn = Vsn1, name = N1},
#release{erts_vsn = Vsn2, name = N2}, RUs, Ws,
- _Opts) when Vsn1 /= Vsn2 ->
- {RUs++[[restart_new_emulator]], [{erts_vsn_changed, {N1, N2}} | Ws]};
+ Opts) when Vsn1 /= Vsn2 ->
+ %% Automatically insert a restart_new_emulator instruction when
+ %% erts version is changed. Also allow extra restart at the end of
+ %% the upgrade if restart_emulator option is given.
+ NewRUs = [[restart_new_emulator]|RUs],
+ NewWs = [{erts_vsn_changed, {{N1,Vsn1}, {N2,Vsn2}}} | Ws],
+ check_for_restart_emulator_opt(NewRUs, NewWs, Opts);
check_for_emulator_restart(_, _, RUs, Ws, Opts) ->
+ check_for_restart_emulator_opt(RUs, Ws, Opts).
+
+check_for_restart_emulator_opt(RUs, Ws, Opts) ->
case get_opt(restart_emulator, Opts) of
- true -> {RUs++[[restart_new_emulator]], Ws};
+ true -> {RUs++[[restart_emulator]], Ws};
_ -> {RUs, Ws}
end.
+
+%% Special handling of the upgrade from pre R15 to post R15. In R15,
+%% upgrade of the emulator was improved by moving the restart of the
+%% emulator before the rest of the upgrade instructions. However, it
+%% can only work if the release_handler is already upgraded to a post
+%% R15 version. If not, the upgrade instructions must be backwards
+%% compatible - i.e. restart_new_emulator will be the last
+%% instruction, executed after all code loading, code_change etc.
+fix_r15_sasl_upgrade([restart_new_emulator | RestRUs]=RUs, Ws, BaseApps) ->
+ case lists:keyfind(sasl,1,BaseApps) of
+ {sasl,Vsn} when Vsn < ?R15_SASL_VSN ->
+ {lists:delete(restart_emulator,RestRUs) ++ [restart_new_emulator],
+ [pre_R15_emulator_upgrade|Ws]};
+ _ ->
+ {RUs,Ws}
+ end;
+fix_r15_sasl_upgrade(RUs, Ws, _BaseApps) ->
+ {RUs,Ws}.
+
+
%% collect_appup_scripts(Mode, TopApps, BaseRel, Ws, RUs) -> {NRUs, NWs}
%% Mode = up | dn
%% TopApps = [#application]
@@ -440,13 +486,32 @@ get_script_from_appup(Mode, TopApp, BaseVsn, Ws, RUs) ->
%% XXX Why is this a warning only?
[{bad_vsn, {TopVsn, TopApp#application.vsn}}| Ws]
end,
- case lists:keysearch(BaseVsn, 1, VsnRUs) of
- {value, {_, RU}} ->
+ case appup_search_for_version(BaseVsn, VsnRUs) of
+ {ok, RU} ->
{RUs ++ [RU], Ws1};
- _ ->
+ error ->
throw({error, ?MODULE, {no_relup, FName, TopApp, BaseVsn}})
end.
+appup_search_for_version(BaseVsn, VsnRUs) ->
+ appup_search_for_version(BaseVsn, length(BaseVsn), VsnRUs).
+
+appup_search_for_version(BaseVsn,_,[{BaseVsn,RU}|_]) ->
+ {ok,RU};
+appup_search_for_version(BaseVsn,Size,[{Vsn,RU}|VsnRUs]) when is_binary(Vsn) ->
+ case re:run(BaseVsn,Vsn,[unicode,{capture,first,index}]) of
+ {match,[{0,Size}]} ->
+ {ok, RU};
+ _ ->
+ appup_search_for_version(BaseVsn,Size,VsnRUs)
+ end;
+appup_search_for_version(BaseVsn,Size,[_|VsnRUs]) ->
+ appup_search_for_version(BaseVsn,Size,VsnRUs);
+appup_search_for_version(_,_,[]) ->
+ error.
+
+
+
%% Primitives for the "lists of release names" that we upgrade from
%% and to.
@@ -543,7 +608,9 @@ format_error({no_relup, File, App, Vsn}) ->
"in file ~p~n",
[App#application.name, App#application.vsn,
App#application.name, Vsn, File]);
-
+format_error({missing_sasl,Release}) ->
+ io_lib:format("No sasl application in release ~p, ~p. Can not be upgraded.",
+ [Release#release.name, Release#release.vsn]);
format_error(Error) ->
io:format("~p~n", [Error]).
diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile
index 65be134462..91a8c42484 100644
--- a/lib/sasl/test/Makefile
+++ b/lib/sasl/test/Makefile
@@ -36,6 +36,8 @@ MODULES= \
ERL_FILES= $(MODULES:%=%.erl)
+HRL_FILES= test_lib.hrl
+
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
INSTALL_PROGS= $(TARGET_FILES)
@@ -84,7 +86,7 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) $(RELSYSDIR)
chmod -R u+w $(RELSYSDIR)
@tar cfh - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl
index f5ceab0dc4..6942ec21ea 100644
--- a/lib/sasl/test/installer.erl
+++ b/lib/sasl/test/installer.erl
@@ -19,20 +19,33 @@
-module(installer).
+-include("test_lib.hrl").
+
%%-compile(export_all).
-export([install_1/2]).
-export([install_2/1]).
-export([install_3/2]).
--export([install_3a/1]).
+-export([install_6a/1]).
-export([install_4/1]).
-export([install_5/1]).
-export([install_5a/1]).
-export([install_6/1]).
-export([install_7/1]).
+-export([install_7a/1]).
-export([install_8/1]).
+-export([install_8a/1]).
-export([install_9/1]).
-export([install_10/1]).
-export([install_11/1]).
+-export([install_12/1]).
+-export([install_13/1]).
+-export([install_14/1]).
+-export([upgrade_restart_1/2]).
+-export([upgrade_restart_1a/1]).
+-export([upgrade_restart_2/1]).
+-export([upgrade_restart_2a/1]).
+-export([upgrade_restart_2b/1]).
+-export([upgrade_restart_3/1]).
-export([client1_1/4]).
-export([client2/3]).
-export([stop/1]).
@@ -46,28 +59,35 @@
-define(fail(Term), exit({?MODULE, ?LINE, Term})).
-define(fail_line(Line,Term), exit({?MODULE, Line, Term})).
--define(check_release(Vsn,Status,Apps),
- check_release(TestNode,node(),Vsn,Status,Apps,?LINE)).
--define(check_release_client(Node,Vsn,Status,Apps),
- check_release(TestNode,Node,Vsn,Status,Apps,?LINE)).
+-define(check_release_states(States),
+ check_release_states(TestNode,node(),States,?LINE)).
+-define(check_release_states_client(Node,States),
+ check_release_states(TestNode,Node,States,?LINE)).
+
+-define(check_release_lib(Vsn,Apps),
+ check_release_lib(TestNode,node(),Vsn,Apps,?LINE)).
+-define(check_release_lib_client(Node,Vsn,Apps),
+ check_release_lib(TestNode,Node,Vsn,Apps,?LINE)).
-define(check_running_app(App,Vsn),
check_running_app(TestNode,node(),App,Vsn,?LINE)).
-define(check_running_app_client(Node,App,Vsn),
check_running_app(TestNode,Node,App,Vsn,?LINE)).
+-define(check_disallowed_calls,check_disallowed_calls(TestNode,?LINE)).
+
install_1(TestNode,PrivDir) ->
?print([TestNode]),
?print(["install_1 start"]),
+ ?check_release_states([permanent]),
% Unpack and install P1H
{ok, "P1H"} = unpack_release(PrivDir,"rel1"),
- ?print(["unpack_release P1H ok"]),
- ?check_release("P1H",unpacked,["a-1.0"]),
+ ?check_release_states([permanent,unpacked]),
+ ?check_release_lib("P1H",["a-1.0"]),
{ok,"P1G",[new_appl]} = release_handler:install_release("P1H"),
- ?print(["install_release P1H ok"]),
- ?check_release("P1H",current,["a-1.0"]),
+ ?check_release_states([permanent,current]),
?check_running_app(a,"1.0"),
X = a:a(),
?print(["X", X]),
@@ -81,173 +101,351 @@ install_2(TestNode) ->
?print(["install_2 start"]),
% Check that P1H is still unpacked, install it and make_permanent
- ?check_release("P1H",unpacked,["a-1.0"]),
- ?print(["install_2 P1H unpacked"]),
+ ?check_release_states([permanent,unpacked]),
{ok,"P1G",[new_appl]} = release_handler:install_release("P1H"),
?print(["install_2 install_release ok"]),
- ?check_release("P1H",current,["a-1.0"]),
+ ?check_release_states([permanent,current]),
+ ?check_running_app(a,"1.0"),
+ ok = release_handler:make_permanent("P1H"),
+ ?print(["install_2 make permanent P1H ok"]),
+ ?check_release_states([old,permanent]),
?check_running_app(a,"1.0"),
- ok = release_handler:make_permanent("P1H").
+ ok.
% release_handler_SUITE will reboot this node now!
install_3(TestNode,PrivDir) ->
?print(["install_3 start"]),
% Check that P1H is permanent
- ?check_release("P1H",permanent,["a-1.0"]),
+ ?check_release_states([old,permanent]),
+ ?check_running_app(a,"1.0"),
X = a:a(),
{key2, val2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
% Unpack and install P1I
{ok, "P1I"} = unpack_release(PrivDir,"rel2"),
- ?print(["install_3 unpack_release P1I ok"]),
- ?check_release("P1I",unpacked,["a-1.1"]),
+ ?check_release_states([old,permanent,unpacked]),
+ ?check_release_lib("P1I",["a-1.1"]),
{ok,"P1H",[{extra, gott}]} = release_handler:check_install_release("P1I"),
+ ?print(["install_3 check_install_release P1I ok"]),
{error,_} = release_handler:check_install_release("P1J"),
+ ?print(["install_3 check_install_release P1J fails - ok"]),
{ok,"P1H",[{extra, gott}]} = release_handler:install_release("P1I"),
- ?print(["install_3 install_release P1I ok"]),
- ?check_release("P1I",current,["a-1.1"]),
+ ?check_release_states([old,permanent,current]),
?check_running_app(a,"1.1"),
X2 = a:a(),
{key2, newval2} = lists:keyfind(key2, 1, X2),
{key1, val1} = lists:keyfind(key1, 1, X2),
{ok, bval} = a:b(),
+ ?print(["install_3 env ok"]),
- % Unpack and install P2A
+ % Unpack P2A
{ok, "P2A"} = unpack_release(PrivDir,"rel3"),
- ?print(["install_3 unpack_release P2A ok"]),
- ?check_release("P2A",unpacked,["a-1.1"]),
+ ?check_release_states([old,permanent,current,unpacked]),
+ ?check_release_lib("P2A",["a-1.1"]),
{ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"),
?print(["install_3 check_install_release P2A ok"]),
- ok = release_handler:make_permanent("P1I"),
- ?print(["install_3 make_permanent P1I ok"]),
- ?check_release("P1I",permanent,["a-1.1"]),
ok.
+ % release_handler_SUITE will reboot this node now!
+
+install_4(TestNode) ->
+ ?print(["install_4 start"]),
-install_3a(TestNode) ->
- {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"),
+ %% Check that P1H is the one that is used
+ ?check_release_states([old,permanent,unpacked,unpacked]),
+ ?check_running_app(a,"1.0"),
+
+ %% Install P2A
+ {continue_after_restart, "P1H", [new_emu,new_appl]} =
+ release_handler:install_release("P2A"),
%% Node is rebooted by the release_handler:install_release
%% (init:reboot) because P2A includes a new erts vsn and the relup
%% file contains a 'restart_new_emulator' instruction.
- ?print(["install_3 P2A installed"]),
+ ?print(["install_4 P2A installed"]),
ok.
+install_5(TestNode) ->
+ ?print(["install_5 start"]),
-install_4(TestNode) ->
- ?print(["install_4 start"]),
+ %% Check that the upgrade was done via a temporary release due to
+ %% new emulator version.
+ {"SASL-test","__new_emulator__P1H"} = init:script_id(),
+
+ %% Check that P2A is in use.
+ ?check_release_states([old,permanent,unpacked,current]),
+ ?check_running_app(a,"1.1"),
+ X = a:a(),
+ {key2, newval2} = lists:keyfind(key2, 1, X),
+ {key1, val1} = lists:keyfind(key1, 1, X),
+ {ok, bval} = a:b(),
+ ?print(["install_5 check env ok"]),
+ ok.
+
+install_5a(TestNode) ->
+ ?print(["install_5a start"]),
+
+ %% Install P1I (this will be a downgrade)
+ {ok, "P1I", [old_emu]} = release_handler:install_release("P1I"),
+ %% Node is rebooted by the release_handler:install_release
+ %% (init:reboot) because P2A includes a new erts vsn and the relup
+ %% file contains a 'restart_new_emulator' instruction.
+ ?print(["install_5a P1I installed"]),
+ ok.
+
+install_6(TestNode) ->
+ ?print(["install_6 start"]),
+
+ %% Check that P1I is used
+ ?check_release_states([old,permanent,current,old]),
+ ?check_running_app(a,"1.1"),
+
+ %% Make P1I permanent
+ ok = release_handler:make_permanent("P1I"),
+ ?check_release_states([old,old,permanent,old]),
+ ?check_running_app(a,"1.1"),
+ ok.
+
+install_6a(TestNode) ->
+ %% Install P2A
+ {continue_after_restart, "P1I", [new_emu]} =
+ release_handler:install_release("P2A"),
+ %% Node is rebooted by the release_handler:install_release
+ %% (init:reboot) because P2A includes a new erts vsn and the relup
+ %% file contains a 'restart_new_emulator' instruction.
+ ?print(["install_6a P2A installed"]),
+ ok.
+
+install_7(TestNode) ->
+ ?print(["install_7 start"]),
+
+ %% Check that the upgrade was done via a temporary release due to
+ %% new emulator version.
+ {"SASL-test","__new_emulator__P1I"} = init:script_id(),
% Check that P2A is in use.
- ?check_release("P2A",current,["a-1.1"]),
+ ?check_release_states([old,old,permanent,current]),
?check_running_app(a,"1.1"),
X = a:a(),
{key2, newval2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
{ok, bval} = a:b(),
+ ?print(["install_7 check env ok"]),
ok.
- % release_handler_SUITE will reboot this node now!
-install_5(TestNode) ->
- ?print(["install_5 start"]),
+install_7a(TestNode) ->
+ %% Install P1H (this will be a downgrade)
+ {ok, "P1H", [old_emu,old_appl]} = release_handler:install_release("P1H"),
+ %% Node is rebooted by the release_handler:install_release
+ %% (init:reboot) because P2A includes a new erts vsn and the relup
+ %% file contains a 'restart_new_emulator' instruction.
+ ?print(["install_7a P1H installed"]),
+ ok.
- % Check that P1I is used
- {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"),
+install_8(TestNode) ->
+ ?print(["install_8 start"]),
+
+ %% Check that P1H is used
+ ?check_release_states([old,current,permanent,old]),
+ ?check_running_app(a,"1.0"),
+ {ok,"P1H",[new_emu,new_appl]} = release_handler:check_install_release("P2A"),
+ ?print(["install_8 check_install_release P2A ok"]),
+
+ %% Install P1I and check that it is permanent
+ {ok,"P1H",[{extra, gott}]} = release_handler:install_release("P1I"),
+ ?check_release_states([old,old,permanent,old]),
+ ?check_running_app(a,"1.1"),
ok.
-install_5a(TestNode) ->
+install_8a(TestNode) ->
% Install P2A again
- {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"),
+ {continue_after_restart, "P1I", [new_emu]} =
+ release_handler:install_release("P2A"),
%% Node is rebooted by the release_handler:install_release
%% (init:reboot) because P2A includes a new erts vsn and the relup
%% file contains a 'restart_new_emulator' instruction.
- ?print(["install_5 P2A installed"]),
+ ?print(["install_8a P2A installed"]),
ok.
-install_6(TestNode) ->
- ?print(["install_6 start"]),
+install_9(TestNode) ->
+ ?print(["install_9 start"]),
+
+ %% Check that the upgrade was done via a temporary release due to
+ %% new emulator version.
+ {"SASL-test","__new_emulator__P1I"} = init:script_id(),
% Check that P2A is used
- ?check_release("P2A",current,["a-1.1"]),
+ ?check_release_states([old,old,permanent,current]),
?check_running_app(a,"1.1"),
X = a:a(),
{key2, newval2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
{ok, bval} = a:b(),
- ok = release_handler:make_permanent("P2A").
+ ?print(["install_9 check env ok"]),
+ ok = release_handler:make_permanent("P2A"),
+ ?check_release_states([old,old,old,permanent]),
+ ?check_running_app(a,"1.1"),
+ ok.
% release_handler_SUITE will reboot this node now!
-install_7(TestNode) ->
- ?print(["install_7 start"]),
+install_10(TestNode) ->
+ ?print(["install_10 start"]),
% Check that P2A is used
- ?check_release("P2A",permanent,["a-1.1"]),
+ ?check_release_states([old,old,old,permanent]),
+ ?check_running_app(a,"1.1"),
% Install old P1H
ok = release_handler:reboot_old_release("P1H"),
+ ?print(["install_10 reboot_old ok"]),
ok.
-install_8(TestNode) ->
- ?print(["install_8 start"]),
+
+install_11(TestNode) ->
+ ?print(["install_11 start"]),
% Check that P1H is permanent
- ?check_release("P1H",permanent,["a-1.0"]),
+ ?check_release_states([old,permanent,old,old]),
+ ?check_running_app(a,"1.0"),
X = a:a(),
{key2, val2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
+ ?print(["install_11 check env ok"]),
%% Remove P1I and P2A and check that a-1.1 and erts-<latest> are removed
ok = release_handler:remove_release("P2A"),
+ ?check_release_states([old,permanent,old]),
ok = release_handler:remove_release("P1I"),
+ ?check_release_states([old,permanent]),
{ok, Libs} = file:list_dir(code:lib_dir()),
{_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()),
true = lists:member("stdlib-"++StdlibVsn, Libs),
true = lists:member("a-1.0", Libs),
false = lists:member("a-1.1", Libs),
{ok, Dirs} = file:list_dir(code:root_dir()),
- ["erts-4.4"] = lists:filter(fun(Dir) -> lists:prefix("erts-",Dir) end, Dirs),
+ ErtsDir = "erts-"++?ertsvsn,
+ [ErtsDir] = lists:filter(fun(Dir) -> lists:prefix("erts-",Dir) end, Dirs),
+ ?print(["install_11 file checks ok"]),
ok.
% release_handler_SUITE will reboot this node now!
-install_9(TestNode) ->
- ?print(["install_9 start"]),
+install_12(TestNode) ->
+ ?print(["install_12 start"]),
% Check that P1H is permanent
- ?check_release("P1H",permanent,["a-1.0"]),
+ ?check_release_states([old,permanent]),
+ ?check_running_app(a,"1.0"),
X = a:a(),
{key2, val2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
+ ?print(["install_12 check env ok"]),
% Install old P1G
ok = release_handler:reboot_old_release("P1G"),
+ ?print(["install_12 reboot_old ok"]),
ok.
-install_10(TestNode) ->
- ?print(["install_10 start"]),
+install_13(TestNode) ->
+ ?print(["install_13 start"]),
% Check that P1G is permanent
- ?check_release("P1G",permanent,[]),
- ?check_release("P1H",old,["a-1.0"]),
+ ?check_release_states([permanent,old]),
+ false = lists:keysearch(a,1,application:loaded_applications()),
+ ?print(["install_13 no a application found - ok"]),
%% Remove P1H and check that both versions of application a is removed
ok = release_handler:remove_release("P1H"),
+ ?check_release_states([permanent]),
{ok, Libs} = file:list_dir(code:lib_dir()),
{_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()),
true = lists:member("stdlib-"++StdlibVsn, Libs),
false = lists:member("a-1.0", Libs),
false = lists:member("a-1.1", Libs),
+ ?print(["install_13 file checks ok"]),
ok.
% release_handler_SUITE will reboot this node now!
-install_11(TestNode) ->
- ?print(["install_11 start"]),
+install_14(TestNode) ->
+ ?print(["install_14 start"]),
% Check that P1G is permanent
- ?check_release("P1G",permanent,[]),
+ ?check_release_states([permanent]),
+ false = lists:keysearch(a,1,application:loaded_applications()),
+ ?print(["install_13 no a application found - ok"]),
ok.
+%%%-----------------------------------------------------------------
+%%% Ths test checks that an upgrade which both upgrades to a new
+%%% emulator version, and had a restart_emulator option to
+%%% systools:make_relup will be restarted twice on upgrade.
+%%% (On downgrade it will happen only once.)
+upgrade_restart_1(TestNode,PrivDir) ->
+ ?print([TestNode]),
+ ?print(["upgrade_restart_1 start"]),
+ ?check_release_states([permanent]),
+
+ {ok, "P2B"} = unpack_release(PrivDir,"rel4"),
+ ?check_release_states([permanent,unpacked]),
+ ?check_release_lib("P2B",["a-1.1"]),
+ ok.
+
+upgrade_restart_1a(TestNode) ->
+ ?print(["upgrade_restart_1a start"]),
+
+ {continue_after_restart,"P1G",[new_emu,add_appl]} =
+ release_handler:install_release("P2B"),
+ ?print(["upgrade_restart_1a P2B installed"]),
+ ok.
+
+upgrade_restart_2(TestNode) ->
+ ?print(["upgrade_restart_2 start"]),
+
+ %% Check that the node has been restarted once more after the tmp release
+ case init:script_id() of
+ {"SASL-test","P2B"} ->
+ upgrade_restart_2a(TestNode);
+ {"SASL-test","__new_emulator__P1G"} ->
+ %% catched the node too early - give it another try
+ {wait,whereis(init)}
+ end.
+
+upgrade_restart_2a(TestNode) ->
+ ?print(["upgrade_restart_2a start"]),
+
+ %% This time we must be there, else something is definitely wrong
+ {"SASL-test","P2B"} = init:script_id(),
+
+ ?check_release_states([permanent,current]),
+ ?check_running_app(a,"1.1"),
+
+ ok = release_handler:make_permanent("P2B"),
+ ?check_release_states([old,permanent]),
+
+ ok.
+
+upgrade_restart_2b(TestNode) ->
+ ?print(["upgrade_restart_2b start"]),
+
+ {ok,"P1G",[old_emu,rm_appl]} = release_handler:install_release("P1G"),
+ ?print(["upgrade_restart_2b P1G installed"]),
+ ok.
+
+upgrade_restart_3(TestNode) ->
+ ?print(["upgrade_restart_3 start"]),
+
+ %% Ideally we should test that the node has only been restarted
+ %% once... but that's not so easy. Let's just check that P1G is running.
+ ?check_release_states([current,permanent]),
+ false = lists:keysearch(a,1,application:loaded_applications()),
+ ?print(["upgrade_restart_3 no a application found - ok"]),
+
+ ok.
+
+
+
%%-----------------------------------------------------------------
%% This test starts a client node which uses this node as master
@@ -272,6 +470,8 @@ client1_1(TestNode,PrivDir,MasterDir,ClientSname) ->
Node = start_client(TestNode,client1,ClientSname),
trace_disallowed_calls(Node),
+ ?check_release_states_client(Node,[permanent]),
+
%% Check env var for SASL on client node
SaslEnv = rpc:call(Node, application, get_all_env, [sasl]),
?print([{client1_1,sasl_env},SaslEnv]),
@@ -300,13 +500,14 @@ client1_1(TestNode,PrivDir,MasterDir,ClientSname) ->
%% as default. But it is given here in order to force hitting the
%% release_handler:check_path function so it can be checked that
%% it does not use file:read_file_info on the client node, see
- %% trace_disallowed_calls/1 and check_disallowed_calls/0 below.
+ %% trace_disallowed_calls/1 and check_disallowed_calls/2 below.
%% (OTP-9142)
{ok, "P1H"} = rpc:call(Node, release_handler, set_unpacked,
[filename:join(P1HDir, "rel1.rel"),
[{a,"1.0",filename:join(MasterDir,lib)}]]),
- ?check_release_client(Node,"P1H",unpacked,["a-1.0"]),
+ ?check_release_states_client(Node,[permanent,unpacked]),
+ ?check_release_lib_client(Node,"P1H",["a-1.0"]),
ok = rpc:call(Node, release_handler, install_file,
["P1H", filename:join(P1HDir, "start.boot")]),
@@ -323,13 +524,16 @@ client1_1(TestNode,PrivDir,MasterDir,ClientSname) ->
{ok,"P1G",[new_appl]} =
rpc:call(Node, release_handler, install_release, ["P1H"]),
+ ?check_release_states_client(Node,[permanent,current]),
+ ?check_running_app_client(Node,a,"1.0"),
+
Apps = rpc:call(Node, application, which_applications, []),
{a,"A CXC 138 11","1.0"} = lists:keyfind(a, 1, Apps),
X = rpc:call(Node, a, a, []),
{key2, val2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
- check_disallowed_calls(),
+ ?check_disallowed_calls,
reboot(TestNode,Node),
trace_disallowed_calls(Node),
@@ -339,17 +543,17 @@ client1_2(TestNode,PrivDir,Node) ->
?print(["client1_2 start"]),
%% Check that P1H is still unpacked, install it and make_permanent
- ?check_release_client(Node,"P1H",unpacked,["a-1.0"]),
+ ?check_release_states_client(Node,[permanent,unpacked]),
{ok,"P1G",[new_appl]} =
rpc:call(Node, release_handler, install_release, ["P1H"]),
- ?check_release_client(Node,"P1H",current,["a-1.0"]),
+ ?check_release_states_client(Node,[permanent,current]),
?check_running_app_client(Node,a,"1.0"),
ok = rpc:call(Node, release_handler, make_permanent, ["P1H"]),
- ?check_release_client(Node,"P1H",permanent,["a-1.0"]),
+ ?check_release_states_client(Node,[old,permanent]),
- check_disallowed_calls(),
+ ?check_disallowed_calls,
reboot(TestNode,Node),
trace_disallowed_calls(Node),
@@ -359,10 +563,8 @@ client1_3(TestNode,PrivDir,Node) ->
?print(["client1_3 start"]),
%% Check that P1H is permanent
- ?check_release_client(Node,"P1H",permanent,["a-1.0"]),
- X = rpc:call(Node, a, a, []),
- {key2, val2} = lists:keyfind(key2, 1, X),
- {key1, val1} = lists:keyfind(key1, 1, X),
+ ?check_release_states_client(Node,[old,permanent]),
+ ?check_running_app_client(Node,a,"1.0"),
%% Unpack P1I on master
{ok, "P1I"} = unpack_release(PrivDir,"rel2"),
@@ -374,7 +576,8 @@ client1_3(TestNode,PrivDir,Node) ->
{ok, "P1I"} = rpc:call(Node, release_handler, set_unpacked,
[filename:join(P1IDir, "rel2.rel"),[]]),
- ?check_release_client(Node,"P1I",unpacked,["a-1.1"]),
+ ?check_release_states_client(Node,[old,permanent,unpacked]),
+ ?check_release_lib_client(Node,"P1I",["a-1.1"]),
ok = rpc:call(Node, release_handler, install_file,
["P1I", filename:join(P1IDir, "start.boot")]),
@@ -389,6 +592,7 @@ client1_3(TestNode,PrivDir,Node) ->
{ok,"P1H",[{extra, gott}]} =
rpc:call(Node, release_handler, install_release, ["P1I"]),
+ ?check_release_states_client(Node,[old,permanent,current]),
?check_running_app_client(Node,a,"1.1"),
X2 = rpc:call(Node, a, a, []),
{key2, newval2} = lists:keyfind(key2, 1, X2),
@@ -404,6 +608,9 @@ client1_3(TestNode,PrivDir,Node) ->
rpc:call(Node, release_handler, set_unpacked,
[filename:join(P2ADir, "rel3.rel"),[]]),
+ ?check_release_states_client(Node,[old,permanent,current,unpacked]),
+ ?check_release_lib_client(Node,"P2A",["a-1.1"]),
+
ok = rpc:call(Node, release_handler, install_file,
["P2A", filename:join(P2ADir, "start.boot")]),
ok = rpc:call(Node, release_handler, install_file,
@@ -413,66 +620,136 @@ client1_3(TestNode,PrivDir,Node) ->
{ok, "P1I", [new_emu]} =
rpc:call(Node, release_handler, check_install_release, ["P2A"]),
+
+ %% Reboot from P1H
+ ?check_disallowed_calls,
+ reboot(TestNode,Node),
+ trace_disallowed_calls(Node),
+
+ client1_4(TestNode,Node).
+
+client1_4(TestNode,Node) ->
+ ?print(["client1_4 start"]),
+
+ %% check that P1H is used
+ ?check_release_states_client(Node,[old,permanent,unpacked,unpacked]),
+
+ %% since the install_release below reboot the node...
+ ?check_disallowed_calls,
+ cover_client(TestNode,Node,stop_cover),
+
+ {continue_after_restart, "P1H", [new_emu,new_appl]} =
+ rpc:call(Node, release_handler, install_release, ["P2A"]),
+ %% Reboots the client !
+
+ check_reboot(TestNode,Node),
+ trace_disallowed_calls(Node),
+
+ client1_5(TestNode,Node).
+
+client1_5(TestNode,Node) ->
+ ?print(["client1_5 start"]),
+
+ %% Check that P2A is in use.
+ ?check_release_states_client(Node,[old,permanent,unpacked,current]),
+ ?check_running_app_client(Node,a,"1.1"),
+ X = rpc:call(Node, a, a, []),
+ {key2, newval2} = lists:keyfind(key2, 1, X),
+ {key1, val1} = lists:keyfind(key1, 1, X),
+ {ok, bval} = rpc:call(Node, a, b, []),
+
+ %% since the install_release below reboot the node...
+ ?check_disallowed_calls,
+ cover_client(TestNode,Node,stop_cover),
+
+ {ok,"P1I",[old_emu]} =
+ rpc:call(Node, release_handler, install_release, ["P1I"]),
+
+ check_reboot(TestNode,Node),
+ trace_disallowed_calls(Node),
+
+ client1_6(TestNode,Node).
+
+client1_6(TestNode,Node) ->
+ ?print(["client1_6 start"]),
+
+ ?check_release_states_client(Node,[old,permanent,current,old]),
+ ?check_running_app_client(Node,a,"1.1"),
+
ok = rpc:call(Node, release_handler, make_permanent, ["P1I"]),
- ?check_release_client(Node,"P1I",permanent,["a-1.1"]),
+ ?check_release_states_client(Node,[old,old,permanent,old]),
%% since the install_release below reboot the node...
- check_disallowed_calls(),
+ ?check_disallowed_calls,
cover_client(TestNode,Node,stop_cover),
- {ok, "P1I", [new_emu]} =
+ {continue_after_restart, "P1I", [new_emu]} =
rpc:call(Node, release_handler, install_release, ["P2A"]),
%% Reboots the client !
check_reboot(TestNode,Node),
trace_disallowed_calls(Node),
- client1_4(TestNode,Node).
+ client1_7(TestNode,Node).
-client1_4(TestNode,Node) ->
- ?print(["client1_4 start"]),
+client1_7(TestNode,Node) ->
+ ?print(["client1_7 start"]),
%% Check that P2A is in use.
- ?check_release_client(Node,"P2A",current,["a-1.1"]),
+ ?check_release_states_client(Node,[old,old,permanent,current]),
?check_running_app_client(Node,a,"1.1"),
X = rpc:call(Node, a, a, []),
{key2, newval2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
{ok, bval} = rpc:call(Node, a, b, []),
- %% Reboot from P1I
- check_disallowed_calls(),
- reboot(TestNode,Node),
+ %% since the install_release below reboot the node...
+ ?check_disallowed_calls,
+ cover_client(TestNode,Node,stop_cover),
+
+ {ok,"P1H",[old_emu,old_appl]} =
+ rpc:call(Node, release_handler, install_release, ["P1H"]),
+
+ check_reboot(TestNode,Node),
trace_disallowed_calls(Node),
- client1_5(TestNode,Node).
+ client1_8(TestNode,Node).
-client1_5(TestNode,Node) ->
- ?print(["client1_5 start"]),
+client1_8(TestNode,Node) ->
+ ?print(["client1_8 start"]),
- %% Check that P1I is used
- {ok, "P1I", [new_emu]} =
+ %% Check that P1H is used
+ ?check_release_states_client(Node,[old,current,permanent,old]),
+ ?check_running_app_client(Node,a,"1.0"),
+ {ok, "P1H", [new_emu,new_appl]} =
rpc:call(Node, release_handler, check_install_release, ["P2A"]),
+
+ {ok,"P1H",[{extra, gott}]} =
+ rpc:call(Node, release_handler, install_release, ["P1I"]),
+ ?check_release_states_client(Node,[old,old,permanent,old]),
+ ?check_running_app_client(Node,a,"1.1"),
+
+
%% since the install_release below will reboot the node...
- check_disallowed_calls(),
+ ?check_disallowed_calls,
cover_client(TestNode,Node,stop_cover),
%% Install P2A again
- {ok, "P1I", [new_emu]} =
+ {continue_after_restart, "P1I", [new_emu]} =
rpc:call(Node, release_handler, install_release, ["P2A"]),
%% We are rebooted again.
check_reboot(TestNode,Node),
trace_disallowed_calls(Node),
- client1_6(TestNode,Node).
+ client1_9(TestNode,Node).
-client1_6(TestNode,Node) ->
- ?print(["client1_6 start"]),
+client1_9(TestNode,Node) ->
+ ?print(["client1_9 start"]),
%% Check that P2A is used
- ?check_release_client(Node,"P2A",current,["a-1.1"]),
+ ?check_release_states_client(Node,[old,old,permanent,current]),
?check_running_app_client(Node,a,"1.1"),
X = rpc:call(Node, a, a, []),
{key2, newval2} = lists:keyfind(key2, 1, X),
@@ -481,22 +758,23 @@ client1_6(TestNode,Node) ->
%% Make P2A permanent
ok = rpc:call(Node, release_handler, make_permanent, ["P2A"]),
+ ?check_release_states_client(Node,[old,old,old,permanent]),
%% Reboot from P2A
- check_disallowed_calls(),
+ ?check_disallowed_calls,
reboot(TestNode,Node),
trace_disallowed_calls(Node),
- client1_7(TestNode,Node).
+ client1_10(TestNode,Node).
-client1_7(TestNode,Node) ->
- ?print(["client1_7 start"]),
+client1_10(TestNode,Node) ->
+ ?print(["client1_10 start"]),
%% Check that P2A is used
- ?check_release_client(Node,"P2A",permanent,["a-1.1"]),
+ ?check_release_states_client(Node,[old,old,old,permanent]),
%% since the reboot_old_release below will reboot the node
- check_disallowed_calls(),
+ ?check_disallowed_calls,
cover_client(TestNode,Node,stop_cover),
%% Install old P1H
@@ -505,41 +783,45 @@ client1_7(TestNode,Node) ->
check_reboot(TestNode,Node),
trace_disallowed_calls(Node),
- client1_8(TestNode,Node).
+ client1_11(TestNode,Node).
-client1_8(TestNode,Node) ->
- ?print(["client1_8 start"]),
+client1_11(TestNode,Node) ->
+ ?print(["client1_11 start"]),
%% Check that P1H is permanent
- ?check_release_client(Node,"P1H",permanent,["a-1.0"]),
+ ?check_release_states_client(Node,[old,permanent,old,old]),
+ ?check_running_app_client(Node,a,"1.0"),
X = rpc:call(Node, a, a, []),
{key2, val2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
- %% Remove P1I and P2I from client
+ %% Remove P1I and P2A from client
ok = rpc:call(Node, release_handler, set_removed, ["P2A"]),
+ ?check_release_states_client(Node,[old,permanent,old]),
ok = rpc:call(Node, release_handler, set_removed, ["P1I"]),
+ ?check_release_states_client(Node,[old,permanent]),
- check_disallowed_calls(),
- reboot(TestNode,Node),
- trace_disallowed_calls(Node),
-
- client1_9(TestNode,Node).
-
-client1_9(TestNode,Node) ->
- ?print(["client1_9 start"]),
-
- %% Check that P2A and P1I does not exists and that PiH is permanent.
+ %% Check that P2A and P1I does not exists
Rels = rpc:call(Node, release_handler, which_releases, []),
false = lists:keysearch("P2A", 2, Rels),
false = lists:keysearch("P1I", 2, Rels),
- ?check_release_client(Node,"P1H",permanent,["a-1.0"]),
X = rpc:call(Node, a, a, []),
{key2, val2} = lists:keyfind(key2, 1, X),
{key1, val1} = lists:keyfind(key1, 1, X),
+ ?check_disallowed_calls,
+ reboot(TestNode,Node),
+ trace_disallowed_calls(Node),
+
+ client1_12(TestNode,Node).
+
+client1_12(TestNode,Node) ->
+ ?print(["client1_12 start"]),
+
+ ?check_release_states_client(Node,[old,permanent]),
+
%% since the reboot_old_release below will reboot the node
- check_disallowed_calls(),
+ ?check_disallowed_calls,
cover_client(TestNode,Node,stop_cover),
%% Install old P1G
@@ -548,33 +830,34 @@ client1_9(TestNode,Node) ->
check_reboot(TestNode,Node),
trace_disallowed_calls(Node),
- client1_10(TestNode,Node).
+ client1_13(TestNode,Node).
-client1_10(TestNode,Node) ->
- ?print(["client1_10 start"]),
+client1_13(TestNode,Node) ->
+ ?print(["client1_13 start"]),
%% Check that P1G is permanent
- ?check_release_client(Node,"P1G",permanent,[]),
- ?check_release_client(Node,"P1H",old,["a-1.0"]),
+ ?check_release_states_client(Node,[permanent,old]),
{error,client_node} = rpc:call(Node,release_handler,remove_release,["P1H"]),
ok = rpc:call(Node, release_handler, set_removed, ["P1H"]),
+ ?check_release_states_client(Node,[permanent]),
- check_disallowed_calls(),
+ ?check_disallowed_calls,
reboot(TestNode,Node),
trace_disallowed_calls(Node),
- client1_11(TestNode,Node).
+ client1_14(TestNode,Node).
-client1_11(TestNode,Node) ->
- ?print(["client1_11 start"]),
+client1_14(TestNode,Node) ->
+ ?print(["client1_14 start"]),
%% Check that P1G is permanent
- ?check_release_client(Node,"P1G",permanent,[]),
+ ?check_release_states_client(Node,[permanent]),
- check_disallowed_calls(),
+ ?check_disallowed_calls,
stop_client(TestNode,Node), %% TEST IS OK !!
net_kernel:monitor_nodes(false),
+ %% Remove releases from master
ok = release_handler:remove_release("P2A"),
ok = release_handler:remove_release("P1I"),
ok = release_handler:remove_release("P1H"),
@@ -595,9 +878,10 @@ trace_disallowed_calls(Node) ->
rpc:call(Node,dbg,p,[all,call]),
rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]).
-check_disallowed_calls() ->
+check_disallowed_calls(TestNode,Line) ->
receive
Trace when element(1,Trace)==trace ->
+ ?print_line(Line,["Disallowed function called",Trace]),
exit({disallowed_function_call,Trace})
after 0 ->
ok
@@ -628,7 +912,7 @@ start_client_unix(TestNode,Sname,Node) ->
start_client_win32(TestNode,Client,ClientSname) ->
Name = atom_to_list(ClientSname) ++ "_P1G",
RootDir = code:root_dir(),
- ErtsBinDir = filename:join(RootDir,"erts-4.4/bin"),
+ ErtsBinDir = filename:join([RootDir,"erts-"++?ertsvsn,"bin"]),
{ClientArgs,RelClientDir} = rh_test_lib:get_client_args(Client,ClientSname,
RootDir),
@@ -729,8 +1013,10 @@ client2(TestNode,PrivDir,ClientSname) ->
ok
end,
+ %% Unpack P1H on master
{ok, "P1H"} = unpack_release(PrivDir,"rel1"),
+ %% Try to set P1H unpacked on client
Root = code:root_dir(),
{error,{bad_masters,[Master2]}} =
rpc:call(Node, release_handler, set_unpacked,
@@ -755,15 +1041,17 @@ stop(Now) ->
unpack_p1h(TestNode,PrivDir) ->
{ok, "P1H"} = unpack_release(PrivDir,"rel1"),
- ?check_release("P1H",unpacked,["a-1.0"]),
+ ?check_release_states([permanent,unpacked]),
+ ?check_release_lib("P1H",["a-1.0"]),
ok.
permanent_p1h(TestNode) ->
- ?check_release("P1H",unpacked,["a-1.0"]),
+ ?check_release_states([permanent,unpacked]),
+ ?check_release_lib("P1H",["a-1.0"]),
{ok,"P1G",[new_appl]} = release_handler:install_release("P1H"),
- ?check_release("P1H",current,["a-1.0"]),
+ ?check_release_states([permanent,current]),
ok = release_handler:make_permanent("P1H"),
- ?check_release("P1H",permanent,["a-1.0"]),
+ ?check_release_states([old,permanent]),
ok.
@@ -779,24 +1067,38 @@ registered_loop(_Name) ->
exit(killed)
end.
-check_release(TestNode,Node,Vsn,Status,Apps,Line) ->
+%% Checks that the list of states for all releases (sorted on vsn)
+%% equals the input States
+check_release_states(TestNode,Node,States,Line) ->
+ case rpc:call(Node,release_handler,which_releases,[]) of
+ {badrpc,_}=Error ->
+ ?fail_line(Line,{check_release_states,Node,States,Error});
+ Rels ->
+ ?print_line(Line,["check_release_states:", Rels]),
+ States = [Status || {_,_,_,Status} <- lists:keysort(2,Rels)],
+ ok
+ end.
+
+%% Check that the given release (Vsn) sees the correct vsn of App.
+check_release_lib(TestNode,Node,Vsn,Apps,Line) ->
case rpc:call(Node,release_handler,which_releases,[]) of
{badrpc,_}=Error ->
- ?fail_line(Line,{check_release,Node,Vsn,Status,Error});
+ ?fail_line(Line,{check_release_lib,Node,Vsn,Apps,Error});
Rels ->
- ?print_line(Line,["check_release:", Rels]),
- {"SASL-test", Vsn, Libs, Status} = lists:keyfind(Vsn, 2, Rels),
+ ?print_line(Line,["check_release_lib:", Rels]),
+ {"SASL-test", Vsn, Libs, _Status} = lists:keyfind(Vsn, 2, Rels),
true = lists:all(fun(App) -> lists:member(App,Libs) end,Apps),
ok
end.
+%% Check that the given Vsn of App is executed
check_running_app(TestNode,Node,App,Vsn,Line) ->
case rpc:call(Node,application,which_applications,[]) of
{badrpc,_}=Error ->
?fail_line(Line,{check_running_app,Node,App,Vsn,Error});
Apps ->
?print_line(Line,["check_running_app:", Apps]),
- {App, _, Vsn} = lists:keyfind(a, 1, Apps),
+ {App, _, Vsn} = lists:keyfind(App, 1, Apps),
ok
end.
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 286aff9bb0..11f8bbe4fe 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -19,6 +19,7 @@
-module(release_handler_SUITE).
-include_lib("common_test/include/ct.hrl").
+-include("test_lib.hrl").
-compile(export_all).
@@ -30,6 +31,7 @@ suite() ->
[{ct_hooks, [ts_install_cth]}].
init_per_suite(Config) ->
+ init_priv_dir(Config),
application:start(sasl),
Config.
@@ -59,7 +61,7 @@ cases() ->
otp_9395_check_old_code, otp_9395_check_and_purge,
otp_9395_update_many_mods, otp_9395_rm_many_mods,
instructions, eval_appup, supervisor_which_children_timeout,
- release_handler_which_releases].
+ release_handler_which_releases, install_release_syntax_check].
groups() ->
[{release,[],
@@ -70,6 +72,7 @@ groups() ->
{release_single,[],
[
upgrade,
+ upgrade_restart,
client1,
client2
]},
@@ -83,7 +86,7 @@ groups() ->
init_per_group(release, Config) ->
Dog = ?t:timetrap(?default_timeout),
P1gInstall = filename:join(priv_dir(Config),p1g_install),
- ok = do_create_p1g(Config,P1gInstall),
+ ok = create_p1g(Config,P1gInstall),
ok = create_p1h(Config),
?t:timetrap_cancel(Dog);
@@ -96,6 +99,7 @@ init_per_group(release_single, Config) ->
%% Create some more releases to upgrade to
ok = create_p1i(Config),
ok = create_p2a(Config),
+ ok = create_p2b(Config),
?t:timetrap_cancel(Dog);
@@ -156,7 +160,7 @@ end_per_group(release, Config) ->
{win32,_} -> delete_all_services();
_ -> ok
end,
- delete_release(Config),
+ clean_priv_dir(Config,true),
?t:timetrap_cancel(Dog),
Config;
end_per_group(_GroupName, Config) ->
@@ -192,7 +196,10 @@ end_per_testcase(Case, Config) ->
FailDir = filename:join(SaveDir,lists:concat(["failed-",Case])),
ok = filelib:ensure_dir(filename:join(FailDir,"*")),
- LogDirs = filelib:wildcard(filename:join([PrivDir,"*",log])),
+ LogDirs =
+ filelib:wildcard(filename:join([PrivDir,"*",log])) ++
+ filelib:wildcard(filename:join([PrivDir,"*",clients,
+ type1,"*",log])),
lists:foreach(
fun(LogDir) ->
@@ -237,7 +244,7 @@ break(Config) ->
?t:break(priv_dir(Config)),
ok.
-%% Test upgrade and downgrade of erts
+%% Test upgrade and downgrade of erts and other apps on embedded node
upgrade(Conf) when is_list(Conf) ->
reg_print_proc(), %% starts a printer process on test_server node
?t:format("upgrade ~p~n",[reg_print_proc]),
@@ -260,54 +267,75 @@ upgrade(Conf) when is_list(Conf) ->
stop_cover(TestNode),
reboot_and_wait(TestNode,"install_2",[a]),
- %% check that P1H is permanent, unpack and install P1I, unpack and install P2A
- TestNodeInit1 = rpc:call(TestNode,erlang,whereis,[init]),
+ %% check that P1H is permanent, unpack and install P1I, unpack P2A
ok = rpc_inst(TestNode, install_3, [PrivDir]),
stop_cover(TestNode),
- ok = rpc_inst(TestNode, install_3a, []),
- wait_nodes_up([{TestNode,TestNodeInit1}],"install_3",[a]),
+ reboot_and_wait(TestNode,"install_3",[a]),
- %% check that P2A is used, reboot from P1I
- ok = rpc_inst(TestNode, install_4, []),
+ %% check that P1H is used, install P2A
+ TestNodeInit1 = rpc:call(TestNode,erlang,whereis,[init]),
stop_cover(TestNode),
- reboot_and_wait(TestNode,"install_4",[a]),
+ ok = rpc_inst(TestNode, install_4, []),
+ wait_nodes_up([{TestNode,TestNodeInit1}],"install_4",[a]),
- %% check that P1I, reinstall P2A
+ %% check that P2A is used, then downgrade to P1I
TestNodeInit2 = rpc:call(TestNode,erlang,whereis,[init]),
ok = rpc_inst(TestNode, install_5, []),
stop_cover(TestNode),
ok = rpc_inst(TestNode, install_5a, []),
wait_nodes_up([{TestNode,TestNodeInit2}],"install_5",[a]),
- %% check that P2A is used, make P2A permanent
+ %% Check that P1I is used, then make P1I permanent and install P2A
+ TestNodeInit3 = rpc:call(TestNode,erlang,whereis,[init]),
ok = rpc_inst(TestNode, install_6, []),
stop_cover(TestNode),
- reboot_and_wait(TestNode,"install_6",[a]),
+ ok = rpc_inst(TestNode, install_6a, []),
+ wait_nodes_up([{TestNode,TestNodeInit3}],"install_6",[a]),
- %% check that P2A is permanent, install old P1H
- TestNodeInit3 = rpc:call(TestNode,erlang,whereis,[init]),
- stop_cover(TestNode),
+ %% check that P2A is used, then downgrade to P1H
+ TestNodeInit4 = rpc:call(TestNode,erlang,whereis,[init]),
ok = rpc_inst(TestNode, install_7, []),
- wait_nodes_up([{TestNode,TestNodeInit3}],"install_7",[a]),
+ stop_cover(TestNode),
+ ok = rpc_inst(TestNode, install_7a, []),
+ wait_nodes_up([{TestNode,TestNodeInit4}],"install_7",[a]),
- %% check that P1H is permanent, remove P1I and P2A
+ %% check that P1H is used, then install P1I and check that it is permanent
+ %% then reinstall P2A
+ TestNodeInit5 = rpc:call(TestNode,erlang,whereis,[init]),
ok = rpc_inst(TestNode, install_8, []),
stop_cover(TestNode),
- reboot_and_wait(TestNode,"install_8",[a]),
+ ok = rpc_inst(TestNode, install_8a, []),
+ wait_nodes_up([{TestNode,TestNodeInit5}],"install_8",[a]),
+
+ %% check that P2A is used, make P2A permanent
+ ok = rpc_inst(TestNode, install_9, []),
+ stop_cover(TestNode),
+ reboot_and_wait(TestNode,"install_9",[a]),
+
+ %% check that P2A is permanent, reboot to old P1H
+ TestNodeInit6 = rpc:call(TestNode,erlang,whereis,[init]),
+ stop_cover(TestNode),
+ ok = rpc_inst(TestNode, install_10, []),
+ wait_nodes_up([{TestNode,TestNodeInit6}],"install_10",[a]),
+
+ %% check that P1H is permanent, remove P1I and P2A
+ ok = rpc_inst(TestNode, install_11, []),
+ stop_cover(TestNode),
+ reboot_and_wait(TestNode,"install_11",[a]),
%% check that P1H is permanent, reboot old P1G
- TestNodeInit4 = rpc:call(TestNode,erlang,whereis,[init]),
+ TestNodeInit7 = rpc:call(TestNode,erlang,whereis,[init]),
stop_cover(TestNode),
- ok = rpc_inst(TestNode, install_9, []),
- wait_nodes_up([{TestNode,TestNodeInit4}],"install_9"),
+ ok = rpc_inst(TestNode, install_12, []),
+ wait_nodes_up([{TestNode,TestNodeInit7}],"install_12"),
%% check that P1G is permanent, remove P1H
- ok = rpc_inst(TestNode, install_10, []),
+ ok = rpc_inst(TestNode, install_13, []),
stop_cover(TestNode),
- reboot_and_wait(TestNode,"install_10"),
+ reboot_and_wait(TestNode,"install_13"),
%% check that P1G is permanent
- ok = rpc_inst(TestNode, install_11, []),
+ ok = rpc_inst(TestNode, install_14, []),
ok.
@@ -324,6 +352,54 @@ reboot_and_wait(Node,Tag,Apps) ->
wait_nodes_up([{Node,InitPid}],Tag,Apps).
+%% Test upgrade and downgrade of erts in combination with the
+%% restart_emulator option to systools:make_relup. For upgrade, this
+%% should cause one restart before the upgrade code, and one
+%% after. For downgrade, there will be one restart only - at the end.
+upgrade_restart(Conf) when is_list(Conf) ->
+ reg_print_proc(), %% starts a printer process on test_server node
+ ?t:format("upgrade_restart ~p~n",[reg_print_proc]),
+ PrivDir = priv_dir(Conf),
+ Sname = tc_sname(Conf), % nodename for use in this testcase
+
+ %% Copy the P1G release to a directory for use in this testcase
+ ok = copy_installed(Conf,p1g_install,[Sname]),
+
+ %% start the test node
+ [TestNode] = start_nodes(Conf,[Sname],"upgrade_restart start"),
+
+ %% unpack and install P2B
+ TestNodeInit1 = rpc:call(TestNode,erlang,whereis,[init]),
+ ok = rpc_inst(TestNode, upgrade_restart_1, [PrivDir]),
+ stop_cover(TestNode),
+ ok = rpc_inst(TestNode, upgrade_restart_1a, []),
+ wait_nodes_up([{TestNode,TestNodeInit1}],"upgrade_restart_1",[a]),
+
+ %% install P1G
+ case rpc_inst(TestNode, upgrade_restart_2, []) of
+ ok ->
+ ok;
+ {wait,TestNodeInit2a} ->
+ %% We catched the node too early - it was supposed to
+ %% restart twice, so let's wait for one more restart.
+ wait_nodes_up([{TestNode,TestNodeInit2a}],"upgrade_restart_2a",[]),
+ ok = rpc_inst(TestNode, upgrade_restart_2a, [])
+ end,
+ TestNodeInit2 = rpc:call(TestNode,erlang,whereis,[init]),
+ stop_cover(TestNode),
+ ok = rpc_inst(TestNode, upgrade_restart_2b, []),
+ wait_nodes_up([{TestNode,TestNodeInit2}],"upgrade_restart_2b",[]),
+
+ %% Check that P1G is going again
+ ok = rpc_inst(TestNode, upgrade_restart_3, []),
+
+ ok.
+
+upgrade_restart(cleanup,Config) ->
+ TestNode = tc_full_node_name(Config),
+ ok = stop_nodes([TestNode]).
+
+
%% Test upgrade and downgrade of erts, diskless
client1(Conf) when is_list(Conf) ->
reg_print_proc(), %% starts a printer process on test_server node
@@ -543,9 +619,25 @@ supervisor_which_children_timeout(Conf) ->
ok.
-supervisor_which_children_timeout(cleanup, Conf) ->
+supervisor_which_children_timeout(cleanup, _Conf) ->
stop_node(node_name(supervisor_which_children_timeout)).
+
+%% Test that check_install_release will fail for illegal relup
+%% instructions, even after point of no return.
+install_release_syntax_check(Conf) when is_list(Conf) ->
+
+ S1 = [point_of_no_return, illegal_instruction],
+ {error,{illegal_instruction_after_point_of_no_return,illegal_instruction}} =
+ release_handler_1:check_script(S1,[]),
+
+ S2 = [point_of_no_return,restart_new_emulator],
+ {error,{illegal_instruction_after_point_of_no_return,restart_new_emulator}} =
+ release_handler_1:check_script(S2,[]),
+
+ ok.
+
+
%%-----------------------------------------------------------------
%% release_handler:which_releases/0 and 1 test
%%-----------------------------------------------------------------
@@ -1174,11 +1266,10 @@ target_system(Conf) when is_list(Conf) ->
%% Create the .rel file
- ErtsVsn = erlang:system_info(version),
RelName = filename:join(TargetCreateDir,"ts-1.0"),
RelFile = RelName++".rel",
RelVsn = "R1A",
- create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,[{a, "1.0"}]),
+ create_rel_file(RelFile,RelName,RelVsn,current,[{a, "1.0"}]),
%% Build the target_system module
ExamplesEbin = filename:join([code:lib_dir(sasl),examples,ebin]),
@@ -1206,11 +1297,12 @@ target_system(Conf) when is_list(Conf) ->
code:del_path(TSPath),
%% Check that all files exist in installation
- true = filelib:is_dir(filename:join(TargetInstallDir,"erts-"++ErtsVsn)),
+ ErtsDir = app_dir(erts,current),
+ true = filelib:is_dir(filename:join(TargetInstallDir,ErtsDir)),
LibDir = filename:join(TargetInstallDir,lib),
- {ok,KernelVsn} = application:get_key(kernel,vsn),
- {ok,StdlibVsn} = application:get_key(stdlib,vsn),
- {ok,SaslVsn} = application:get_key(sasl,vsn),
+ KernelVsn = vsn(kernel,current),
+ StdlibVsn = vsn(stdlib,current),
+ SaslVsn = vsn(sasl,current),
true = filelib:is_dir(filename:join(LibDir,"kernel-"++KernelVsn)),
true = filelib:is_dir(filename:join(LibDir,"stdlib-"++StdlibVsn)),
true = filelib:is_dir(filename:join(LibDir,"sasl-"++SaslVsn)),
@@ -1232,6 +1324,7 @@ target_system(Conf) when is_list(Conf) ->
true = filelib:is_regular(filename:join(BinDir,to_erl)),
%% Check content of files
+ ErtsVsn = vsn(erts,current),
{ok,SED} = file:read_file(filename:join(RelDir,"start_erl.data")),
[ErtsVsn,RelVsn] = string:tokens(binary_to_list(SED),"\s\n"),
ok.
@@ -1483,7 +1576,7 @@ copy_client(Conf,Master,Sname,Client) ->
ok.
-delete_release(Conf) ->
+clean_priv_dir(Conf,Save) ->
PrivDir = priv_dir(Conf),
{ok, OrigWd} = file:get_cwd(),
@@ -1493,7 +1586,7 @@ delete_release(Conf) ->
{ok, Dirs} = file:list_dir(PrivDir),
?t:format("======== deleting ~p~n",[Dirs]),
- ok = delete_release_os(Dirs--["save"]),
+ ok = clean_dirs_os(Dirs,Save),
{ok,Remaining} = file:list_dir(PrivDir),
?t:format("======== remaining ~p~n",[Remaining]),
@@ -1501,7 +1594,7 @@ delete_release(Conf) ->
[] ->
ok;
_ ->
- delete_release_os(Remaining),
+ clean_dirs_os(Remaining,Save),
Remaining2 = file:list_dir(PrivDir),
?t:format("======== remaining after second try ~p~n",[Remaining2])
end,
@@ -1510,22 +1603,22 @@ delete_release(Conf) ->
ok.
-delete_release_os(Dirs) ->
+clean_dirs_os(Dirs,Save) ->
case os:type() of
{unix, _} ->
- delete_release_unix(Dirs);
+ clean_dirs_unix(Dirs,Save);
{win32, _} ->
- delete_release_win32(Dirs);
+ clean_dirs_win32(Dirs,Save);
Os ->
test_server:fail({error, {not_yet_implemented_os, Os}})
end.
-delete_release_unix([]) ->
+clean_dirs_unix([],_) ->
ok;
-delete_release_unix(["save"|Dirs]) ->
- delete_release_unix(Dirs);
-delete_release_unix([Dir|Dirs]) ->
+clean_dirs_unix(["save"|Dirs],Save) when Save ->
+ clean_dirs_unix(Dirs,Save);
+clean_dirs_unix([Dir|Dirs],Save) ->
Rm = string:concat("rm -rf ", Dir),
?t:format("============== COMMAND ~p~n",[Rm]),
case file:list_dir(Dir) of
@@ -1542,13 +1635,13 @@ delete_release_unix([Dir|Dirs]) ->
?t:format("------- ls -al ~p~n",[os:cmd("ls -al " ++ Dir)])
end,
- delete_release_unix(Dirs).
+ clean_dirs_unix(Dirs,Save).
-delete_release_win32([]) ->
+clean_dirs_win32([],_) ->
ok;
-delete_release_win32(["save"|Dirs]) ->
- delete_release_win32(Dirs);
-delete_release_win32([Dir|Dirs]) ->
+clean_dirs_win32(["save"|Dirs],Save) when Save ->
+ clean_dirs_win32(Dirs,Save);
+clean_dirs_win32([Dir|Dirs],Save) ->
Rm =
case filelib:is_dir(Dir) of
true ->
@@ -1558,7 +1651,7 @@ delete_release_win32([Dir|Dirs]) ->
end,
?t:format("============== COMMAND ~p~n",[Rm]),
[] = os:cmd(Rm),
- delete_release_win32(Dirs).
+ clean_dirs_win32(Dirs,Save).
node_name(Sname) when is_atom(Sname) ->
@@ -1684,9 +1777,17 @@ priv_dir(Conf) ->
%% filename:absname(?config(priv_dir, Conf)). % Get rid of trailing slash
%% Due to problem with long paths on windows => creating a new
%% priv_dir under data_dir
+ filename:absname(filename:join(?config(data_dir, Conf),priv_dir)).
+
+init_priv_dir(Conf) ->
Dir = filename:absname(filename:join(?config(data_dir, Conf),priv_dir)),
- filelib:ensure_dir(filename:join(Dir,"*")),
- Dir.
+ case filelib:is_dir(Dir) of
+ true ->
+ clean_priv_dir(Conf,false);
+ false ->
+ ok
+ end,
+ filelib:ensure_dir(filename:join(Dir,"*")).
latest_version(Dir) ->
List = filelib:wildcard(Dir ++ "*"),
@@ -1721,14 +1822,22 @@ stop_print_proc() ->
%% Create the first target release, vsn P1G. This release is used for
%% all test cases in {group,release}
-create_p1g(Conf,Sname) ->
- do_create_p1g(Conf,filename:join(priv_dir(Conf),Sname)).
-
-do_create_p1g(Conf,TargetDir) ->
- PrivDir = priv_dir(Conf),
+create_p1g(Conf,TargetDir) ->
DataDir = ?config(data_dir,Conf),
- ErtsVsn = "4.4",
- ErtsDir = "erts-"++ErtsVsn,
+ PrivDir = priv_dir(Conf),
+ ErtsDir = app_dir(erts,old),
+ KernelDir = app_dir(kernel,old),
+ StdlibDir = app_dir(stdlib,old),
+
+ %% Fake earlier version of kernel and stdlib
+ SystemLib = system_lib(PrivDir),
+ ok = filelib:ensure_dir(filename:join(SystemLib,"*")),
+ KernelLib = code:lib_dir(kernel),
+ StdlibLib = code:lib_dir(stdlib),
+ ok = copy_tree(Conf,KernelLib,KernelDir,SystemLib),
+ ok = copy_tree(Conf,StdlibLib,StdlibDir,SystemLib),
+ fix_version(SystemLib,kernel),
+ fix_version(SystemLib,stdlib),
%% Create dirs
BinDir = filename:join(TargetDir,bin),
@@ -1772,17 +1881,15 @@ do_create_p1g(Conf,TargetDir) ->
RelFileName = filename:join(RelDir,RelName),
RelFile = RelFileName ++ ".rel",
ok = filelib:ensure_dir(RelFile),
- LibPath = filename:join([DataDir,lib,"*",ebin]),
- TarFile = create_basic_release(Conf, RelFile, RelVsn, {ErtsVsn,false},
- LibPath, [], [], [], []),
+ TarFile = create_basic_release(Conf,RelFile,RelVsn,{old,false}),
%% Extract tar file in target directory (i.e. same directory as erts etc.)
ok = erl_tar:extract(TarFile, [{cwd, TargetDir}, compressed]),
%% Create start_erl.data
StartErlDataFile = filename:join([ReleasesDir, "start_erl.data"]),
- StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]),
+ StartErlData = io_lib:fwrite("~s ~s~n", [vsn(erts,old), RelVsn]),
ok = file:write_file(StartErlDataFile, StartErlData),
%% Create RELEASES
@@ -1790,60 +1897,98 @@ do_create_p1g(Conf,TargetDir) ->
ok.
+fix_version(SystemLib,App) ->
+ FromVsn = vsn(App,current),
+ ToVsn = vsn(App,old),
+ Rootname = filename:join([SystemLib,app_dir(App,old),ebin,atom_to_list(App)]),
+
+ AppFile = Rootname ++ ".app",
+ {ok,OrigApp} = file:read_file(AppFile),
+ ok = file:write_file(AppFile,re:replace(OrigApp,FromVsn,ToVsn,
+ [{return,binary}])),
+ AppupFile = Rootname ++ ".appup",
+ {ok,OrigAppup} = file:read_file(AppupFile),
+ ok = file:write_file(AppupFile,re:replace(OrigAppup,FromVsn,ToVsn,
+ [{return,binary}])).
+
+
%% Create version P1H - which is P1G + a-1.0
%% Must have run create_p1g first!!
create_p1h(Conf) ->
- create_upgrade_release(Conf,"rel1","P1H",{"4.4",false},[{a,"1.0"}],
- [{a,[{key2,val2}]}],{"rel0",[new_appl]}).
+ create_upgrade_release(Conf,"rel1","P1H",{old,false},[{a,"1.0"}],
+ [{a,[{key2,val2}]}],[{"rel0",[new_appl]}]).
%% Create version P1I - which is P1H, but with application a upgraded to a-1.1
%% Must have run create_p1h first!!
create_p1i(Conf) ->
- create_upgrade_release(Conf,"rel2","P1I",{"4.4",false},[{a,"1.1"}],
+ create_upgrade_release(Conf,"rel2","P1I",{old,false},[{a,"1.1"}],
[{a,[{key2,newval2}]}],
- {"rel1",[{extra,gott}]}).
+ [{"rel1",[{extra,gott}]}]).
%% Create version P2A - which is P1I, but with erts-<latest>
%% Must have run create_p1i first!!
create_p2a(Conf) ->
- ErtsVsn = erlang:system_info(version),
- create_upgrade_release(Conf,"rel3","P2A",{ErtsVsn,code:root_dir()},
+ create_upgrade_release(Conf,"rel3","P2A",{current,code:root_dir()},
+ [{a,"1.1"}],[{a,[{key2,newval2}]}],
+ [{"rel1",[new_emu,new_appl]},{"rel2",[new_emu]}],
+ [{"rel1",[old_emu,old_appl]},{"rel2",[old_emu]}]).
+
+%% Create version P2B - which is P2A, but with relup containing an
+%% extra reboot.
+%% Can be upgraded to from P1G - so must have run create_p1g first!!
+create_p2b(Conf) ->
+ create_upgrade_release(Conf,"rel4","P2B",{current,code:root_dir()},
[{a,"1.1"}],[{a,[{key2,newval2}]}],
- {"rel2",[new_emu]}).
+ [{"rel0",[new_emu,add_appl]}],
+ [{"rel0",[old_emu,rm_appl]}],
+ [restart_emulator]).
%% Create a release tar package which can be installed on top of P1G
-create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,{UpFromName,Descr}) ->
+create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,UpFrom) ->
+ create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,UpFrom,[]).
+create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,UpFrom,DownTo) ->
+ create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,UpFrom,DownTo,[]).
+create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,UpFrom0,DownTo0,RelupOpts) ->
PrivDir = priv_dir(Conf),
- DataDir = ?config(data_dir,Conf),
-
RelDir = filename:join(PrivDir,RelName),
RelFileName = filename:join(RelDir,RelName),
RelFile = RelFileName ++ ".rel",
ok = filelib:ensure_dir(RelFile),
- LibPath = filename:join([DataDir,lib,"*",ebin]),
- UpFrom = [{filename:join([PrivDir,UpFromName,UpFromName]),Descr}],
+ UpFrom = [{filename:join([PrivDir,UpFromName,UpFromName]),Descr} ||
+ {UpFromName,Descr} <- UpFrom0],
+ DownTo = [{filename:join([PrivDir,DownToName,DownToName]),Descr} ||
+ {DownToName,Descr} <- DownTo0],
- create_basic_release(Conf, RelFile, RelVsn, Erts, LibPath,
- Apps, Config, UpFrom, []),
+ create_basic_release(Conf,RelFile,RelVsn,Erts,Apps,Config,
+ UpFrom,DownTo,RelupOpts),
ok.
%% Create .rel, .script, .boot, sys.config and tar
-create_basic_release(Conf, RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Config,UpFrom,DownTo) ->
+create_basic_release(Conf,RelFile,RelVsn,{Erts,ErtsDir}) ->
+ create_basic_release(Conf, RelFile,RelVsn,{Erts,ErtsDir},[],[],[],[],[]).
+create_basic_release(Conf,RelFile,RelVsn,{Erts,ErtsDir},ExtraApps,Config,UpFrom,DownTo,RelupOpts) ->
+ DataDir = ?config(data_dir,Conf),
+ PrivDir = priv_dir(Conf),
+ SystemLib = system_lib(PrivDir),
+ LibPath = [filename:join([SystemLib,"*",ebin]),
+ filename:join([DataDir,lib,"*",ebin])],
+
RelDir = filename:dirname(RelFile),
RelFileName = filename:rootname(RelFile),
%% Create .rel file
- create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps),
+ create_installer_rel_file(RelFile,RelVsn,Erts,ExtraApps),
%% Generate .script and .boot
ok = systools:make_script(RelFileName,
- [{path,[LibPath]},
+ [{path,LibPath},
{outdir,RelDir}]),
%% Generate relup
- ok = systools:make_relup(RelFileName,UpFrom,DownTo,[{path,[LibPath]},
- {outdir,RelDir}]),
+ ok = systools:make_relup(RelFileName,UpFrom,DownTo,[{path,LibPath},
+ {outdir,RelDir} |
+ RelupOpts]),
%% Create sys.config
ok = write_term_file(filename:join(RelDir,"sys.config"),Config),
@@ -1851,7 +1996,7 @@ create_basic_release(Conf, RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Co
%% Create tar file (i.e. collect all lib/app-*/* and system files)
ok = systools:make_tar(RelFileName,
- [{path,[LibPath]},
+ [{path,LibPath},
{outdir,RelDir} |
case ErtsDir of
false -> [];
@@ -1868,18 +2013,19 @@ create_basic_release(Conf, RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Co
TarFileName.
%% Create a .rel file
-create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps) ->
- create_rel_file(RelFile,"SASL-test",RelVsn,ErtsVsn,
+create_installer_rel_file(RelFile,RelVsn,Erts,ExtraApps) ->
+ create_rel_file(RelFile,"SASL-test",RelVsn,Erts,
[{installer,"1.0"}|ExtraApps]).
-create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,ExtraApps) ->
- {ok,KernelVsn} = application:get_key(kernel,vsn),
- {ok,StdlibVsn} = application:get_key(stdlib,vsn),
- {ok,SaslVsn} = application:get_key(sasl,vsn),
+create_rel_file(RelFile,RelName,RelVsn,Erts,ExtraApps) ->
+ ErtsVsn = vsn(erts,Erts),
+ KernelVsn = vsn(kernel,Erts),
+ StdlibVsn = vsn(stdlib,Erts),
+ SaslVsn = vsn(sasl,current),
application:load(tools),
- {ok,ToolsVsn} = application:get_key(tools,vsn),
+ ToolsVsn = vsn(tools,current),
application:load(runtime_tools),
- {ok,RuntimeToolsVsn} = application:get_key(runtime_tools,vsn),
+ RuntimeToolsVsn = vsn(runtime_tools,current),
RelFileContent = {release,
{RelName, RelVsn},
@@ -2060,7 +2206,7 @@ start_node_unix(Sname,NodeDir) ->
start_node_win32(Sname,NodeDir) ->
Name = atom_to_list(Sname) ++ "_P1G",
- ErtsBinDir = filename:join(NodeDir,"erts-4.4/bin"),
+ ErtsBinDir = filename:join([NodeDir,app_dir(erts,old),"bin"]),
StartErlArgs = rh_test_lib:get_start_erl_args(NodeDir),
ServiceArgs = rh_test_lib:get_service_args(NodeDir, Sname, StartErlArgs),
@@ -2185,7 +2331,6 @@ create_fake_release(Dir,RelName,RelVsn,AppDirs) ->
RelDir = filename:join(Dir,"rel_" ++ RelVsn),
Rel = filename:join([RelDir,"rel_" ++ RelVsn]),
ok = filelib:ensure_dir(Rel),
- ErtsVsn = erlang:system_info(version),
{Apps,Paths} =
lists:foldl(fun({App,Vsn,Lib},{As,Ps}) ->
@@ -2195,7 +2340,7 @@ create_fake_release(Dir,RelName,RelVsn,AppDirs) ->
{[],[]},
AppDirs),
- create_rel_file(Rel++".rel",RelName,RelVsn,ErtsVsn,Apps),
+ create_rel_file(Rel++".rel",RelName,RelVsn,current,Apps),
%% Generate boot scripts
ok = systools:make_script(Rel,[local,
@@ -2244,3 +2389,16 @@ modify_tar_win32(Conf, TarFileName) ->
[ok = erl_tar:add(T,filename:join(TmpDir,F),F,[]) || F <- Fs],
ok = erl_tar:close(T),
ok.
+
+app_dir(App,Vsn) ->
+ atom_to_list(App) ++ "-" ++ vsn(App,Vsn).
+vsn(erts,old) -> ?ertsvsn;
+vsn(kernel,old) -> ?kernelvsn;
+vsn(stdlib,old) -> ?stdlibvsn;
+vsn(erts,current) -> erlang:system_info(version);
+vsn(App,current) ->
+ {ok,Vsn} = application:get_key(App,vsn),
+ Vsn.
+
+system_lib(PrivDir) ->
+ filename:join(PrivDir,"system_lib").
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup
index 05db4cb541..6ef67b869e 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup
@@ -1,3 +1,3 @@
{"1.1",
[{"1.0",[{update,a,{advanced,extra_par}}]}],
- []}.
+ [{"1.0",[{update,a,{advanced,extra_par}}]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl
index c082ad5339..1050e53f35 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl
@@ -51,4 +51,6 @@ terminate(_Reason, _State) ->
ok.
code_change(1, Extra, State) ->
- {ok, {state, bval}}.
+ {ok, {state, bval}};
+code_change({down,1},Extra,State) ->
+ {ok, state}.
diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl
index 454095db6a..195324daa0 100644
--- a/lib/sasl/test/sasl_SUITE.erl
+++ b/lib/sasl/test/sasl_SUITE.erl
@@ -30,10 +30,11 @@
% Test cases must be exported.
-export([app_test/1,
+ appup_test/1,
log_mf_h_env/1]).
all() ->
- [app_test, log_mf_h_env].
+ [app_test, appup_test, log_mf_h_env].
groups() ->
[].
@@ -57,6 +58,102 @@ app_test(Config) when is_list(Config) ->
?line ?t:app_test(sasl, allow),
ok.
+%% Test that appup allows upgrade from/downgrade to a maximum of two
+%% major releases back.
+appup_test(_Config) ->
+ application:load(sasl),
+ {sasl,_,SaslVsn} = lists:keyfind(sasl,1,application:loaded_applications()),
+ Ebin = filename:join(code:lib_dir(sasl),ebin),
+ {ok,[{SaslVsn,UpFrom,DownTo}=Appup]} =
+ file:consult(filename:join(Ebin,"sasl.appup")),
+ ct:log("~p~n",[Appup]),
+ ?line {OkVsns,NokVsns} = create_test_vsns(SaslVsn),
+ ?line check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
+ ?line check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
+ ?line check_appup(NokVsns,UpFrom,error),
+ ?line check_appup(NokVsns,DownTo,error),
+ ok.
+
+
+%% For sasl, the versions up to R14B03 were not according to the rule
+%% used for other core applications - i.e. to change the second number
+%% at major releases, the third at maintenance releases and the fourth
+%% for patches - therefore test versions up to and including R16 are
+%% hardcoded.
+%% (All versions below are not necessarily existing.)
+-define(r12_vsns,["2.1.5"]).
+-define(r13_vsns,["2.1.6","2.1.7.1","2.1.9","2.1.9.1.2"]).
+-define(r14_vsns,["2.1.9.2","2.1.9.2.20","2.1.9.4","2.1.10"]).
+-define(r15_major,"2.2").
+-define(r16_major,"2.3").
+-define(r17_major,"2.4").
+create_test_vsns(?r15_major ++ Rest) ->
+ R15Vsns =
+ case string:tokens(Rest,".") of
+ [] -> [];
+ ["1"] -> [?r15_major];
+ _ -> [?r15_major,?r15_major++".1"]
+ end,
+ OkVsns = ?r13_vsns ++ ?r14_vsns ++ R15Vsns,
+ NokVsns = ?r12_vsns ++ [?r15_major++",1", ?r16_major],
+ {OkVsns,NokVsns};
+create_test_vsns(?r16_major ++ Rest) ->
+ R16Vsns =
+ case string:tokens(Rest,".") of
+ [] -> [];
+ ["1"] -> [?r16_major];
+ _ -> [?r16_major,?r16_major++".1"]
+ end,
+ OkVsns = ?r14_vsns ++ [?r15_major, ?r15_major ++ ".1.4"] ++ R16Vsns,
+ NokVsns = ?r13_vsns ++ [?r16_major++",1", ?r17_major],
+ {OkVsns,NokVsns};
+%% Normal erts case - i.e. for versions that comply to the erts standard
+create_test_vsns(Current) ->
+ [XStr,YStr|Rest] = string:tokens(Current,"."),
+ X = list_to_integer(XStr),
+ Y = list_to_integer(YStr),
+ SecondMajor = vsn(X,Y-2),
+ SecondMinor = SecondMajor ++ ".1.3",
+ FirstMajor = vsn(X,Y-1),
+ FirstMinor = FirstMajor ++ ".57",
+ ThisMajor = vsn(X,Y),
+ This =
+ case Rest of
+ [] ->
+ [];
+ ["1"] ->
+ [ThisMajor];
+ _ ->
+ ThisMinor = ThisMajor ++ ".1",
+ [ThisMajor,ThisMinor]
+ end,
+ OkVsns = This ++ [FirstMajor, FirstMinor, SecondMajor, SecondMinor],
+
+ ThirdMajor = vsn(X,Y-3),
+ ThirdMinor = ThirdMajor ++ ".10.12",
+ Illegal = ThisMajor ++ ",1",
+ Newer1Major = vsn(X,Y+1),
+ Newer1Minor = Newer1Major ++ ".1",
+ Newer2Major = ThisMajor ++ "1",
+ NokVsns = [ThirdMajor,ThirdMinor,
+ Illegal,
+ Newer1Major,Newer1Minor,
+ Newer2Major],
+ {OkVsns,NokVsns}.
+
+vsn(X,Y) ->
+ integer_to_list(X) ++ "." ++ integer_to_list(Y).
+
+check_appup([Vsn|Vsns],Instrs,Expected) ->
+ case systools_relup:appup_search_for_version(Vsn, Instrs) of
+ Expected -> check_appup(Vsns,Instrs,Expected);
+ Other -> ct:fail({unexpected_result_for_vsn,Vsn,Other})
+ end;
+check_appup([],_,_) ->
+ ok.
+
+
+
%% OTP-9185 - fail sasl start if some but not all log_mf_h env vars
%% are given.
log_mf_h_env(Config) ->
diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl
index e352247d44..892c4994e8 100644
--- a/lib/sasl/test/systools_SUITE.erl
+++ b/lib/sasl/test/systools_SUITE.erl
@@ -41,7 +41,7 @@
-export([all/0,suite/0,groups/0,init_per_group/2,end_per_group/2]).
-export([ script_options/1, normal_script/1, no_mod_vsn_script/1,
- wildcard_script/1, variable_script/1,
+ wildcard_script/1, variable_script/1, no_sasl_script/1,
abnormal_script/1, src_tests_script/1, crazy_script/1,
warn_shadow_script/1,
included_script/1, included_override_script/1,
@@ -49,10 +49,11 @@
-export([ tar_options/1, normal_tar/1, no_mod_vsn_tar/1, variable_tar/1,
src_tests_tar/1, shadow_tar/1, var_tar/1,
exref_tar/1, link_tar/1, otp_9507/1]).
--export([ normal_relup/1, abnormal_relup/1, no_appup_relup/1,
- bad_appup_relup/1, app_start_type_relup/1, otp_3065/1]).
--export([
- otp_6226/1]).
+-export([ normal_relup/1, restart_relup/1, abnormal_relup/1, no_sasl_relup/1,
+ no_appup_relup/1, bad_appup_relup/1, app_start_type_relup/1,
+ regexp_relup/1, otp_3065/1]).
+-export([otp_6226/1]).
+-export([normal_hybrid/1,hybrid_no_old_sasl/1,hybrid_no_new_sasl/1]).
-export([init_per_suite/1, end_per_suite/1,
init_per_testcase/2, end_per_testcase/2]).
@@ -67,15 +68,15 @@ suite() ->
[{ct_hooks, [ts_install_cth]}].
all() ->
- [{group, script}, {group, tar}, {group, relup},
+ [{group, script}, {group, tar}, {group, relup}, {group, hybrid},
{group, tickets}].
groups() ->
[{script, [],
[script_options, normal_script, no_mod_vsn_script,
wildcard_script, variable_script, abnormal_script,
- src_tests_script, crazy_script, warn_shadow_script,
- included_script, included_override_script,
+ no_sasl_script, src_tests_script, crazy_script,
+ warn_shadow_script, included_script, included_override_script,
included_fail_script, included_bug_script, exref_script,
otp_3065]},
{tar, [],
@@ -83,8 +84,10 @@ groups() ->
src_tests_tar, shadow_tar, var_tar,
exref_tar, link_tar, otp_9507]},
{relup, [],
- [normal_relup, abnormal_relup, no_appup_relup,
- bad_appup_relup, app_start_type_relup]},
+ [normal_relup, restart_relup, abnormal_relup, no_sasl_relup,
+ no_appup_relup, bad_appup_relup, app_start_type_relup, regexp_relup
+ ]},
+ {hybrid, [], [normal_hybrid,hybrid_no_old_sasl,hybrid_no_new_sasl]},
{tickets, [], [otp_6226]}].
init_per_group(_GroupName, Config) ->
@@ -388,6 +391,32 @@ abnormal_script(Config) when is_list(Config) ->
%% make_script
%%
+no_sasl_script(suite) -> [];
+no_sasl_script(doc) ->
+ ["Create script without sasl appl. Check warning."];
+no_sasl_script(Config) when is_list(Config) ->
+ ?line {ok, OldDir} = file:get_cwd(),
+
+ ?line {LatestDir, LatestName} = create_script(latest1_no_sasl,Config),
+
+ ?line DataDir = filename:absname(?copydir),
+ ?line LibDir = [fname([DataDir, d_normal, lib])],
+ ?line P = [fname([LibDir, '*', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
+
+ ?line ok = file:set_cwd(LatestDir),
+
+ ?line {ok, _ , [{warning,missing_sasl}]} =
+ systools:make_script(LatestName,[{path, P},silent]),
+
+ ?line ok = file:set_cwd(OldDir),
+ ok.
+
+
+%% make_script
+%%
src_tests_script(suite) -> [];
src_tests_script(doc) ->
["Do not check date of object file or that source code can be found."];
@@ -1106,15 +1135,17 @@ otp_9507(Config) when is_list(Config) ->
RelName = fname([LatestDir,LatestName]),
?line P1 = ["./ebin",
- fname([DataDir, lib, kernel, ebin]),
- fname([DataDir, lib, stdlib, ebin])],
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
?line {ok, _, _} = systools:make_script(RelName, [silent, {path, P1}]),
?line ok = systools:make_tar(RelName, [{path, P1}]),
?line Content1 = tar_contents(RelName),
?line P2 = ["ebin",
- fname([DataDir, lib, kernel, ebin]),
- fname([DataDir, lib, stdlib, ebin])],
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
%% Tickets solves the following line - it used to fail with
%% {function_clause,[{filename,join,[[]]},...}
@@ -1148,18 +1179,11 @@ normal_relup(Config) when is_list(Config) ->
?line LibDir = [fname([DataDir, d_normal, lib])],
?line P = [fname([LibDir, '*', ebin]),
fname([DataDir, lib, kernel, ebin]),
- fname([DataDir, lib, stdlib, ebin])],
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
?line ok = file:set_cwd(LatestDir),
- %% OTP-2561: Check that the option 'restart_emulator' generates a
- %% "restart_new_emulator" instruction.
- ?line {ok, _ , _, []} =
- systools:make_relup(LatestName, [LatestName1], [LatestName1],
- [{path, P},restart_emulator,silent]),
- ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]),
- ?line ok = check_restart_emulator(),
-
%% This is the ultra normal case
?line ok = systools:make_relup(LatestName, [LatestName1], [LatestName1],
[{path, P}]),
@@ -1188,7 +1212,7 @@ normal_relup(Config) when is_list(Config) ->
?line ok = systools:make_relup(LatestName, [LatestName2], [LatestName1],
[{path, P}]),
?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]),
- ?line {ok, _, _, [{erts_vsn_changed, _}]} =
+ ?line {ok, _, _, [pre_R15_emulator_upgrade,{erts_vsn_changed, _}]} =
systools:make_relup(LatestName, [LatestName2], [LatestName1],
[{path, P}, silent]),
?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]),
@@ -1200,6 +1224,91 @@ normal_relup(Config) when is_list(Config) ->
ok.
+restart_relup(suite) -> [];
+restart_relup(doc) ->
+ ["Test relup which includes emulator restart"];
+restart_relup(Config) when is_list(Config) ->
+ ?line {ok, OldDir} = file:get_cwd(),
+
+ ?line {LatestDir,LatestName} = create_script(latest0,Config),
+ ?line {_LatestDir1,LatestName1} = create_script(latest1,Config),
+ ?line {_LatestDir0CurrErts,LatestName0CurrErts} =
+ create_script(latest0_current_erts,Config),
+ ?line {_CurrentAllDir,CurrentAllName} = create_script(current_all,Config),
+ ?line {_CurrentAllFutErtsDir,CurrentAllFutErtsName} =
+ create_script(current_all_future_erts,Config),
+ ?line {_CurrentAllFutSaslDir,CurrentAllFutSaslName} =
+ create_script(current_all_future_sasl,Config),
+
+ ?line DataDir = filename:absname(?copydir),
+ ?line LibDir = [fname([DataDir, d_normal, lib])],
+ ?line P = [fname([LibDir, '*', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin]),
+ fname([DataDir, lib, 'sasl-9.9', ebin])],
+
+ ?line ok = file:set_cwd(LatestDir),
+
+ %% OTP-2561: Check that the option 'restart_emulator' generates a
+ %% "restart_emulator" instruction.
+ ?line {ok, _ , _, []} =
+ systools:make_relup(LatestName, [LatestName1], [LatestName1],
+ [{path, P},restart_emulator,silent]),
+ ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]),
+ ?line ok = check_restart_emulator(),
+
+
+ %% Pre-R15 to Post-R15 upgrade
+ ?line {ok, _ , _, Ws} =
+ systools:make_relup(LatestName0CurrErts,
+ [LatestName1],
+ [LatestName1],
+ [{path, P},silent]),
+ ?line ok = check_relup([{db,"2.1"}], [{db, "1.0"}]),
+ ?line ok = check_pre_to_post_r15_restart_emulator(),
+ ?line ok = check_pre_to_post_r15_warnings(Ws),
+
+
+ %% Check that new sasl version generates a restart_new_emulator
+ %% instruction
+ ?line {ok, _ , _, []} =
+ systools:make_relup(CurrentAllFutSaslName,
+ [CurrentAllName],
+ [CurrentAllName],
+ [{path, P},silent]),
+ ?line ok = check_relup([{fe, "3.1"}], []),
+ ?line ok = check_restart_emulator_diff_coreapp(),
+
+
+ %% Check that new erts version generates a restart_new_emulator
+ %% instruction, if FromSaslVsn >= R15SaslVsn
+ %% (One erts_vsn_changed warning for upgrade and one for downgrade)
+ ?line {ok, _ , _, [{erts_vsn_changed,_},{erts_vsn_changed,_}]} =
+ systools:make_relup(CurrentAllFutErtsName,
+ [CurrentAllName],
+ [CurrentAllName],
+ [{path, P},silent]),
+ ?line ok = check_relup([{fe, "3.1"}], []),
+ ?line ok = check_restart_emulator_diff_coreapp(),
+
+
+ %% Check that new erts version generates a restart_new_emulator
+ %% instruction, and can be combined with restart_emulator opt.
+ %% (One erts_vsn_changed warning for upgrade and one for downgrade)
+ ?line {ok, _ , _, [{erts_vsn_changed,_},{erts_vsn_changed,_}]} =
+ systools:make_relup(CurrentAllFutErtsName,
+ [CurrentAllName],
+ [CurrentAllName],
+ [{path, P},restart_emulator,silent]),
+ ?line ok = check_relup([{fe, "3.1"}], []),
+ ?line ok = check_restart_emulator(),
+ ?line ok = check_restart_emulator_diff_coreapp(),
+
+ ?line ok = file:set_cwd(OldDir),
+ ok.
+
+
%% This test fails if wrong version numbers are seen in the relup file
%% or if any application is missing. This was triggered by OTP-1360.
check_relup(UpVsnL, DnVsnL) ->
@@ -1216,10 +1325,40 @@ check_relup(UpVsnL, DnVsnL) ->
[{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Dn]),
ok.
+check_relup_up_only(UpVsnL) ->
+ {ok, [{_V1, [{_, _, Up}], []}]} = file:consult(relup),
+ [] = foldl(fun(X, Acc) ->
+ true = lists:member(X, Acc),
+ lists:delete(X, Acc) end,
+ UpVsnL,
+ [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Up]),
+ ok.
+
check_restart_emulator() ->
{ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup),
+ restart_emulator = lists:last(Up),
+ restart_emulator = lists:last(Dn),
+ ok.
+
+check_restart_emulator_up_only() ->
+ {ok, [{_V1, [{_, _, Up}], []}]} = file:consult(relup),
+ restart_emulator = lists:last(Up),
+ ok.
+
+check_restart_emulator_diff_coreapp() ->
+ {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup),
+ [restart_new_emulator|_] = Up,
+ restart_emulator = lists:last(Dn),
+ ok.
+
+check_pre_to_post_r15_restart_emulator() ->
+ {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup),
restart_new_emulator = lists:last(Up),
- restart_new_emulator = lists:last(Dn),
+ restart_emulator = lists:last(Dn),
+ ok.
+
+check_pre_to_post_r15_warnings(Ws) ->
+ true = lists:member(pre_R15_emulator_upgrade,Ws),
ok.
%% make_relup
@@ -1235,13 +1374,14 @@ no_appup_relup(Config) when is_list(Config) ->
?line {_LatestDir1,LatestName1} = create_script(latest_small1,Config),
?line DataDir = filename:absname(?copydir),
- ?line P1 = [fname([DataDir, d_no_appup, lib, 'fe-3.1', ebin]),
- fname([DataDir, lib, kernel, ebin]),
- fname([DataDir, lib, stdlib, ebin])],
?line ok = file:set_cwd(LatestDir),
%% Check that appup might be missing
+ ?line P1 = [fname([DataDir, d_no_appup, lib, 'fe-3.1', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
?line ok =
systools:make_relup(LatestName, [LatestName], [], [{path, P1}]),
?line {ok,_, _, []} =
@@ -1249,22 +1389,29 @@ no_appup_relup(Config) when is_list(Config) ->
[silent, {path, P1}]),
%% Check that appup might NOT be missing when we need it
+ ?line P2 = [fname([DataDir, d_no_appup, lib, 'fe-3.1', ebin]),
+ fname([DataDir, d_no_appup, lib, 'fe-2.1', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
?line error =
- systools:make_relup(LatestName, [LatestName0], [], [{path, P1}]),
+ systools:make_relup(LatestName, [LatestName0], [], [{path, P2}]),
?line {error,_,{file_problem, {_,{error,{open,_,_}}}}} =
systools:make_relup(LatestName, [], [LatestName0],
- [silent, {path, P1}]),
+ [silent, {path, P2}]),
%% Check that appups missing vsn traps
- ?line P2 = [fname([DataDir, d_no_appup, lib, 'fe-2.1', ebin]),
+ ?line P3 = [fname([DataDir, d_no_appup, lib, 'fe-2.1', ebin]),
+ fname([DataDir, d_no_appup, lib, 'fe-500.18.7', ebin]),
fname([DataDir, lib, kernel, ebin]),
- fname([DataDir, lib, stdlib, ebin])],
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
?line error =
- systools:make_relup(LatestName0, [LatestName1], [], [{path, P2}]),
+ systools:make_relup(LatestName0, [LatestName1], [], [{path, P3}]),
?line {error,_,{no_relup, _, _, _}} =
systools:make_relup(LatestName0, [], [LatestName1],
- [silent, {path, P2}]),
+ [silent, {path, P3}]),
?line ok = file:set_cwd(OldDir),
ok.
@@ -1282,8 +1429,10 @@ bad_appup_relup(Config) when is_list(Config) ->
?line DataDir = filename:absname(?copydir),
?line N2 = [fname([DataDir, d_bad_appup, lib, 'fe-3.1', ebin]),
+ fname([DataDir, d_bad_appup, lib, 'fe-2.1', ebin]),
fname([DataDir, lib, kernel, ebin]),
- fname([DataDir, lib, stdlib, ebin])],
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
?line ok = file:set_cwd(LatestDir),
@@ -1313,7 +1462,8 @@ abnormal_relup(Config) when is_list(Config) ->
?line P = [fname([DataDir, d_bad_app_vsn, lib, 'db-2.1', ebin]),
fname([DataDir, d_bad_app_vsn, lib, 'fe-3.1', ebin]),
fname([DataDir, lib, kernel, ebin]),
- fname([DataDir, lib, stdlib, ebin])],
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
?line ok = file:set_cwd(LatestDir),
@@ -1329,6 +1479,37 @@ abnormal_relup(Config) when is_list(Config) ->
ok.
+%% make_relup
+%%
+no_sasl_relup(suite) -> [];
+no_sasl_relup(doc) ->
+ ["Check relup can not be created is sasl is not in rel file"];
+no_sasl_relup(Config) when is_list(Config) ->
+ ?line {ok, OldDir} = file:get_cwd(),
+ ?line {Dir1,Name1} = create_script(latest1_no_sasl,Config),
+ ?line {_Dir2,Name2} = create_script(latest1,Config),
+
+ ?line DataDir = filename:absname(?copydir),
+ ?line LibDir = [fname([DataDir, d_normal, lib])],
+ ?line P = [fname([LibDir, '*', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
+
+ ?line ok = file:set_cwd(Dir1),
+
+ ?line error = systools:make_relup(Name2, [Name1], [Name1], [{path, P}]),
+ ?line R1 = systools:make_relup(Name2, [Name1], [Name1],[silent, {path, P}]),
+ ?line {error,systools_relup,{missing_sasl,_}} = R1,
+
+ ?line error = systools:make_relup(Name1, [Name2], [Name2], [{path, P}]),
+ ?line R2 = systools:make_relup(Name1, [Name2], [Name2],[silent, {path, P}]),
+ ?line {error,systools_relup,{missing_sasl,_}} = R2,
+
+ ?line ok = file:set_cwd(OldDir),
+ ok.
+
+
%% Check that application start type is used in relup
app_start_type_relup(suite) ->
[];
@@ -1342,35 +1523,243 @@ app_start_type_relup(Config) when is_list(Config) ->
?line Release2 = filename:join(Dir2,Name2),
?line {ok, Release2Relup, systools_relup, []} = systools:make_relup(Release2, [Release1], [Release1], [{outdir, PrivDir}, silent]),
- ?line {"2", [{"1",[], UpInstructions}], [{"1",[], DownInstructions}]} = Release2Relup,
+ ?line {"LATEST_APP_START_TYPE2",
+ [{"LATEST_APP_START_TYPE1",[], UpInstructions}],
+ [{"LATEST_APP_START_TYPE1",[], DownInstructions}]} = Release2Relup,
%% ?t:format("Up: ~p",[UpInstructions]),
%% ?t:format("Dn: ~p",[DownInstructions]),
?line [{load_object_code, {mnesia, _, _}},
- {load_object_code, {sasl, _, _}},
+ {load_object_code, {runtime_tools, _, _}},
{load_object_code, {webtool, _, _}},
{load_object_code, {snmp, _, _}},
{load_object_code, {xmerl, _, _}},
point_of_no_return
| UpInstructionsT] = UpInstructions,
?line true = lists:member({apply,{application,start,[mnesia,permanent]}}, UpInstructionsT),
- ?line true = lists:member({apply,{application,start,[sasl,transient]}}, UpInstructionsT),
+ ?line true = lists:member({apply,{application,start,[runtime_tools,transient]}}, UpInstructionsT),
?line true = lists:member({apply,{application,start,[webtool,temporary]}}, UpInstructionsT),
?line true = lists:member({apply,{application,load,[snmp]}}, UpInstructionsT),
?line false = lists:any(fun({apply,{application,_,[xmerl|_]}}) -> true; (_) -> false end, UpInstructionsT),
?line [point_of_no_return | DownInstructionsT] = DownInstructions,
?line true = lists:member({apply,{application,stop,[mnesia]}}, DownInstructionsT),
- ?line true = lists:member({apply,{application,stop,[sasl]}}, DownInstructionsT),
+ ?line true = lists:member({apply,{application,stop,[runtime_tools]}}, DownInstructionsT),
?line true = lists:member({apply,{application,stop,[webtool]}}, DownInstructionsT),
?line true = lists:member({apply,{application,stop,[snmp]}}, DownInstructionsT),
?line true = lists:member({apply,{application,stop,[xmerl]}}, DownInstructionsT),
?line true = lists:member({apply,{application,unload,[mnesia]}}, DownInstructionsT),
- ?line true = lists:member({apply,{application,unload,[sasl]}}, DownInstructionsT),
+ ?line true = lists:member({apply,{application,unload,[runtime_tools]}}, DownInstructionsT),
?line true = lists:member({apply,{application,unload,[webtool]}}, DownInstructionsT),
?line true = lists:member({apply,{application,unload,[snmp]}}, DownInstructionsT),
?line true = lists:member({apply,{application,unload,[xmerl]}}, DownInstructionsT),
ok.
+%% regexp_relup
+regexp_relup(Config) ->
+ ?line {ok, OldDir} = file:get_cwd(),
+
+ ?line {LatestDir,LatestName} = create_script(latest_small,Config),
+ ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config),
+ ?line {_LatestDir1,LatestName1} = create_script(latest_small2,Config),
+
+ ?line DataDir = filename:absname(?copydir),
+ ?line P = [fname([DataDir, d_regexp_appup, lib, '*', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
+
+ ?line ok = file:set_cwd(LatestDir),
+
+ %% Upgrade fe 2.1 -> 3.1, and downgrade 2.1 -> 3.1
+ %% Shall match the first entry if fe-3.1 appup.
+ ?line {ok, _, _, []} =
+ systools:make_relup(LatestName, [LatestName0], [LatestName0],
+ [{path, P}, silent]),
+ ?line ok = check_relup([{fe, "3.1"}], [{fe, "2.1"}]),
+
+ %% Upgrade fe 2.1.1 -> 3.1
+ %% Shall match the second entry in fe-3.1 appup. Have added a
+ %% restart_emulator instruction there to distinguish it from
+ %% the first entry...
+ ?line {ok, _, _, []} =
+ systools:make_relup(LatestName, [LatestName1], [], [{path, P}, silent]),
+ ?line ok = check_relup_up_only([{fe, "3.1"}]),
+ ?line ok = check_restart_emulator_up_only(),
+
+ %% Attempt downgrade fe 3.1 -> 2.1.1
+ %% Shall not match any entry!!
+ ?line {error,systools_relup,{no_relup,_,_,_}} =
+ systools:make_relup(LatestName, [], [LatestName1], [{path, P}, silent]),
+
+ ?line ok = file:set_cwd(OldDir),
+
+ ok.
+
+
+%% For upgrade of erts - create a boot file which is a hybrid between
+%% old and new release - i.e. starts erts, kernel, stdlib, sasl from
+%% new release, all other apps from old release.
+normal_hybrid(Config) ->
+ ?line {ok, OldDir} = file:get_cwd(),
+ ?line {Dir1,Name1} = create_script(latest1,Config),
+ ?line {_Dir2,Name2} = create_script(current_all,Config),
+
+ ?line DataDir = filename:absname(?copydir),
+ ?line LibDir = [fname([DataDir, d_normal, lib])],
+ ?line P = [fname([LibDir, '*', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
+
+ ?line ok = file:set_cwd(Dir1),
+
+ ?line {ok, _ , []} = systools:make_script(Name1,[{path, P},silent]),
+ ?line {ok, _ , []} = systools:make_script(Name2,[{path, P},silent]),
+ ?line {ok,Boot1} = file:read_file(Name1 ++ ".boot"),
+ ?line {ok,Boot2} = file:read_file(Name2 ++ ".boot"),
+
+ ?line ok = file:set_cwd(OldDir),
+
+ ?line BasePaths = {"testkernelpath","teststdlibpath","testsaslpath"},
+ ?line {ok,Hybrid} = systools_make:make_hybrid_boot("tmp_vsn",Boot1,Boot2,
+ BasePaths, [dummy,args]),
+
+ ?line {script,{"Test release","tmp_vsn"},Script} = binary_to_term(Hybrid),
+ ct:log("~p.~n",[Script]),
+
+ %% Check that all paths to base apps are replaced by paths from BaseLib
+ Boot1Str = io_lib:format("~p~n",[binary_to_term(Boot1)]),
+ HybridStr = io_lib:format("~p~n",[binary_to_term(Hybrid)]),
+ ReOpts = [global,{capture,first,list},unicode],
+ ?line {match,OldKernelMatch} = re:run(Boot1Str,"kernel-[0-9\.]+",ReOpts),
+ ?line {match,OldStdlibMatch} = re:run(Boot1Str,"stdlib-[0-9\.]+",ReOpts),
+ ?line {match,OldSaslMatch} = re:run(Boot1Str,"sasl-[0-9\.]+",ReOpts),
+
+ ?line nomatch = re:run(HybridStr,"kernel-[0-9\.]+",ReOpts),
+ ?line nomatch = re:run(HybridStr,"stdlib-[0-9\.]+",ReOpts),
+ ?line nomatch = re:run(HybridStr,"sasl-[0-9\.]+",ReOpts),
+ ?line {match,NewKernelMatch} = re:run(HybridStr,"testkernelpath",ReOpts),
+ ?line {match,NewStdlibMatch} = re:run(HybridStr,"teststdlibpath",ReOpts),
+ ?line {match,NewSaslMatch} = re:run(HybridStr,"testsaslpath",ReOpts),
+
+ NewKernelN = length(NewKernelMatch),
+ ?line NewKernelN = length(OldKernelMatch),
+ NewStdlibN = length(NewStdlibMatch),
+ ?line NewStdlibN = length(OldStdlibMatch),
+ NewSaslN = length(NewSaslMatch),
+ ?line NewSaslN = length(OldSaslMatch),
+
+ %% Check that application load instruction has correct versions
+ Apps = application:loaded_applications(),
+ {_,_,KernelVsn} = lists:keyfind(kernel,1,Apps),
+ {_,_,StdlibVsn} = lists:keyfind(stdlib,1,Apps),
+ {_,_,SaslVsn} = lists:keyfind(sasl,1,Apps),
+
+ ?line [KernelInfo] = [I || {kernelProcess,application_controller,
+ {application_controller,start,
+ [{application,kernel,I}]}} <- Script],
+ ?line [StdlibInfo] = [I || {apply,
+ {application,load,
+ [{application,stdlib,I}]}} <- Script],
+ ?line [SaslInfo] = [I || {apply,
+ {application,load,
+ [{application,sasl,I}]}} <- Script],
+
+ ?line {vsn,KernelVsn} = lists:keyfind(vsn,1,KernelInfo),
+ ?line {vsn,StdlibVsn} = lists:keyfind(vsn,1,StdlibInfo),
+ ?line {vsn,SaslVsn} = lists:keyfind(vsn,1,SaslInfo),
+
+ %% Check that new_emulator_upgrade call is added
+ ?line [_,{apply,{release_handler,new_emulator_upgrade,[dummy,args]}}|_] =
+ lists:reverse(Script),
+
+ %% Check that db-1.0 and fe-3.1 are used (i.e. vsns from old release)
+ %% And that fe is in there (it exists in old rel but not in new)
+ ?line {match,DbMatch} = re:run(HybridStr,"db-[0-9\.]+",ReOpts),
+ ?line {match,[_|_]=FeMatch} = re:run(HybridStr,"fe-[0-9\.]+",ReOpts),
+ ?line true = lists:all(fun(["db-1.0"]) -> true;
+ (_) -> false
+ end,
+ DbMatch),
+ ?line true = lists:all(fun(["fe-3.1"]) -> true;
+ (_) -> false
+ end,
+ FeMatch),
+
+ %% Check that script has same length as old script, plus one (the
+ %% new_emulator_upgrade apply)
+ {_,_,Old} = binary_to_term(Boot1),
+ OldLength = length(Old),
+ NewLength = length(Script),
+ ?line NewLength = OldLength + 1,
+
+ ok.
+
+%% Check that systools_make:make_hybrid_boot fails with a meaningful
+%% error message if the FromBoot does not include the sasl
+%% application.
+hybrid_no_old_sasl(Config) ->
+ ?line {ok, OldDir} = file:get_cwd(),
+ ?line {Dir1,Name1} = create_script(latest1_no_sasl,Config),
+ ?line {_Dir2,Name2} = create_script(current_all,Config),
+
+ ?line DataDir = filename:absname(?copydir),
+ ?line LibDir = [fname([DataDir, d_normal, lib])],
+ ?line P = [fname([LibDir, '*', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
+
+ ?line ok = file:set_cwd(Dir1),
+
+ ?line {ok, _ , [{warning,missing_sasl}]} =
+ systools:make_script(Name1,[{path, P},silent]),
+ ?line {ok, _ , []} = systools:make_script(Name2,[{path, P},silent]),
+ ?line {ok,Boot1} = file:read_file(Name1 ++ ".boot"),
+ ?line {ok,Boot2} = file:read_file(Name2 ++ ".boot"),
+
+ ?line BasePaths = {"testkernelpath","teststdlibpath","testsaslpath"},
+ ?line {error,{app_not_replaced,sasl}} =
+ systools_make:make_hybrid_boot("tmp_vsn",Boot1,Boot2,
+ BasePaths,[dummy,args]),
+
+ ?line ok = file:set_cwd(OldDir),
+ ok.
+
+
+%% Check that systools_make:make_hybrid_boot fails with a meaningful
+%% error message if the ToBoot does not include the sasl
+%% application.
+hybrid_no_new_sasl(Config) ->
+ ?line {ok, OldDir} = file:get_cwd(),
+ ?line {Dir1,Name1} = create_script(latest1,Config),
+ ?line {_Dir2,Name2} = create_script(current_all_no_sasl,Config),
+
+ ?line DataDir = filename:absname(?copydir),
+ ?line LibDir = [fname([DataDir, d_normal, lib])],
+ ?line P = [fname([LibDir, '*', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
+
+ ?line ok = file:set_cwd(Dir1),
+
+ ?line {ok, _ , []} = systools:make_script(Name1,[{path, P},silent]),
+ ?line {ok, _ , [{warning,missing_sasl}]} =
+ systools:make_script(Name2,[{path, P},silent]),
+ ?line {ok,Boot1} = file:read_file(Name1 ++ ".boot"),
+ ?line {ok,Boot2} = file:read_file(Name2 ++ ".boot"),
+
+ ?line BasePaths = {"testkernelpath","teststdlibpath","testsaslpath"},
+ ?line {error,{app_not_found,sasl}} =
+ systools_make:make_hybrid_boot("tmp_vsn",Boot1,Boot2,
+ BasePaths,[dummy,args]),
+
+ ?line ok = file:set_cwd(OldDir),
+ ok.
+
+
+
otp_6226(suite) ->
[];
otp_6226(doc) ->
@@ -1388,7 +1777,8 @@ otp_6226(Config) when is_list(Config) ->
fname([LibDir, 'db-1.0', ebin]),
fname([LibDir, 'fe-3.1', ebin]),
fname([DataDir, lib, kernel, ebin]),
- fname([DataDir, lib, stdlib, ebin])],
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
?line ok = file:set_cwd(LatestDir),
@@ -1656,165 +2046,95 @@ tar_name(Name) ->
Name ++ ".tar.gz".
create_script(latest,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, latest),
- ?line Apps = application_controller:which_applications(),
- ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps),
- ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 3\", \"LATEST\"}, \n"
- " {erts, \"4.4\"}, \n"
- " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n"
- " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n",
- [KernelVer,StdlibVer]),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps(current) ++ [{db,"2.1"},{fe,"3.1"}],
+ do_create_script(latest,Config,"4.4",Apps);
create_script(latest_no_mod_vsn,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, latest),
- ?line Apps = application_controller:which_applications(),
- ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps),
- ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 3\", \"LATESTNOMOD\"}, \n"
- " {erts, \"4.4\"}, \n"
- " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n"
- " {db, \"3.1\"}, {fe, \"3.1\"}]}.\n",
- [KernelVer,StdlibVer]),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps(current) ++ [{db,"3.1"},{fe,"3.1"}],
+ do_create_script(latest_no_mod_vsn,Config,"4.4",Apps);
create_script(latest0,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, 'latest-1'),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 2\", \"LATEST0\"}, \n"
- " {erts, \"4.4\"}, \n"
- " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n"
- " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n",
- []),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps("1.0") ++ [{db,"2.1"},{fe,"3.1"}],
+ do_create_script(latest0,Config,"4.4",Apps);
+create_script(latest0_current_erts,Config) ->
+ Apps = core_apps("1.0") ++ [{db,"2.1"},{fe,"3.1"}],
+ do_create_script(latest0_current_erts,Config,current,Apps);
create_script(latest1,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, latest),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 2\", \"LATEST1\"}, \n"
- " {erts, \"4.4\"}, \n"
- " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n"
- " {db, \"1.0\"}, {fe, \"3.1\"}]}.\n",
- []),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps("1.0") ++ [{db,"1.0"},{fe,"3.1"}],
+ do_create_script(latest1,Config,"4.4",Apps);
+create_script(latest1_no_sasl,Config) ->
+ Apps = [{kernel,"1.0"},{stdlib,"1.0"},{db,"1.0"},{fe,"3.1"}],
+ do_create_script(latest1_no_sasl,Config,"4.4",Apps);
create_script(latest2,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, 'latest-2'),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 1\", \"LATEST2\"}, \n"
- " {erts, \"4.3\"}, \n"
- " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n"
- " {db, \"1.0\"}, {fe, \"2.1\"}]}.\n",
- []),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps("1.0") ++ [{db,"1.0"},{fe,"2.1"}],
+ do_create_script(latest2,Config,"4.3",Apps);
create_script(latest_small,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, 'latest-small'),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 2\", \"LATEST_SMALL\"}, \n"
- " {erts, \"4.4\"}, \n"
- " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n"
- " {fe, \"3.1\"}]}.\n",
- []),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps("1.0") ++ [{fe,"3.1"}],
+ do_create_script(latest_small,Config,"4.4",Apps);
create_script(latest_small0,Config) -> %Differs in fe vsn
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, 'latest-small0'),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 2\", \"LATEST_SMALL0\"}, \n"
- " {erts, \"4.4\"}, \n"
- " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n"
- " {fe, \"2.1\"}]}.\n",
- []),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps("1.0") ++ [{fe,"2.1"}],
+ do_create_script(latest_small0,Config,"4.4",Apps);
create_script(latest_small1,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, 'latest-small1'),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 2\", \"LATEST_SMALL1\"}, \n"
- " {erts, \"4.4\"}, \n"
- " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n"
- " {fe, \"500.18.7\"}]}.\n",
- []),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps("1.0") ++ [{fe,"500.18.7"}],
+ do_create_script(latest_small1,Config,"4.4",Apps);
+create_script(latest_small2,Config) ->
+ Apps = core_apps("1.0") ++ [{fe,"2.1.1"}],
+ do_create_script(latest_small2,Config,"4.4",Apps);
create_script(latest_nokernel,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, 'latest-nokernel'),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line io:format(Fd,
- "{release, {\"Test release 3\", \"LATEST_NOKERNEL\"}, \n"
- " {erts, \"4.4\"}, \n"
- " [{db, \"2.1\"}, {fe, \"3.1\"}]}.\n",
- []),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = [{db,"2.1"},{fe,"3.1"}],
+ do_create_script(latest_nokernel,Config,"4.4",Apps);
create_script(latest_app_start_type1,Config) ->
- ?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, latest_app_start_type1),
- ?line ErtsVer = erlang:system_info(version),
- ?line Apps = application_controller:which_applications(),
- ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps),
- ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps),
- ?line {ok,Fd} = file:open(Name++".rel",write),
- ?line RelfileContent =
- {release,{"Test release", "1"},
- {erts,ErtsVer},
- [{kernel,KernelVer},
- {stdlib,StdlibVer}]},
- ?line io:format(Fd,"~p.~n",[RelfileContent]),
- ?line ok = file:close(Fd),
- {filename:dirname(Name), filename:basename(Name)};
+ Apps = core_apps(current),
+ do_create_script(latest_app_start_type1,Config,current,Apps);
create_script(latest_app_start_type2,Config) ->
+ OtherApps = [{mnesia,current,permanent},
+ {runtime_tools,current,transient},
+ {webtool,current,temporary},
+ {snmp,current,load},
+ {xmerl,current,none}],
+ Apps = core_apps(current) ++ OtherApps,
+ do_create_script(latest_app_start_type2,Config,current,Apps);
+create_script(current_all_no_sasl,Config) ->
+ Apps = [{kernel,current},{stdlib,current},{db,"2.1"},{fe,"3.1"}],
+ do_create_script(current_all_no_sasl,Config,current,Apps);
+create_script(current_all,Config) ->
+ Apps = core_apps(current) ++ [{db,"2.1"}],
+ do_create_script(current_all,Config,current,Apps);
+create_script(current_all_future_erts,Config) ->
+ Apps = core_apps(current) ++ [{db,"2.1"},{fe,"3.1"}],
+ do_create_script(current_all_future_erts,Config,"99.99",Apps);
+create_script(current_all_future_sasl,Config) ->
+ Apps = [{kernel,current},{stdlib,current},{sasl,"9.9"},{db,"2.1"},{fe,"3.1"}],
+ do_create_script(current_all_future_sasl,Config,current,Apps).
+
+
+do_create_script(Id,Config,ErtsVsn,AppVsns) ->
?line PrivDir = ?privdir,
- ?line Name = fname(PrivDir, latest_app_start_type2),
- ?line ErtsVer = erlang:system_info(version),
- ?line Apps = application_controller:which_applications(),
- ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps),
- ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps),
- ?line OtherApps = [{mnesia,permanent},
- {sasl,transient},
- {webtool,temporary},
- {snmp,load},
- {xmerl,none}],
- ?line lists:foreach(fun({App,_}) -> application:load(App) end,
- OtherApps),
- ?line Loaded = application:loaded_applications(),
- ?line OtherAppsRel =
- lists:map(fun({App,StartType}) ->
- {_,_,Ver} = lists:keyfind(App,1,Loaded),
- {App,Ver,StartType}
- end,
- OtherApps),
+ ?line Name = fname(PrivDir, Id),
?line {ok,Fd} = file:open(Name++".rel",write),
- ?line RelfileContent =
- {release,{"Test release", "2"},
- {erts,ErtsVer},
- [{kernel,KernelVer},
- {stdlib,StdlibVer} | OtherAppsRel]},
+ ?line RelfileContent =
+ {release,{"Test release", string:to_upper(atom_to_list(Id))},
+ {erts,erts_vsn(ErtsVsn)},
+ app_vsns(AppVsns)},
?line io:format(Fd,"~p.~n",[RelfileContent]),
?line ok = file:close(Fd),
{filename:dirname(Name), filename:basename(Name)}.
+core_apps(Vsn) ->
+ [{App,Vsn} || App <- [kernel,stdlib,sasl]].
+
+app_vsns(AppVsns) ->
+ [{App,app_vsn(App,Vsn)} || {App,Vsn} <- AppVsns] ++
+ [{App,app_vsn(App,Vsn),Type} || {App,Vsn,Type} <- AppVsns].
+app_vsn(App,current) ->
+ application:load(App),
+ {ok,Vsn} = application:get_key(App,vsn),
+ Vsn;
+app_vsn(_App,Vsn) ->
+ Vsn.
+
+erts_vsn(current) -> erlang:system_info(version);
+erts_vsn(Vsn) -> Vsn.
+
+
create_include_files(inc1, Config) ->
?line PrivDir = ?privdir,
?line Name = fname(PrivDir, inc1),
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app
new file mode 100644
index 0000000000..3cb0b0c2cf
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app
@@ -0,0 +1,7 @@
+{application, fe,
+ [{description, "ERICSSON NR FOR FE"},
+ {vsn, "2.1"},
+ {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {registered, []},
+ {applications, []},
+ {mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app
new file mode 100644
index 0000000000..3a5c0ddd9b
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app
@@ -0,0 +1,7 @@
+{application, fe,
+ [{description, "ERICSSON NR FOR FE"},
+ {vsn, "500.18.7"},
+ {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {registered, []},
+ {applications, []},
+ {mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app
new file mode 100644
index 0000000000..c7ba1dfe91
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app
@@ -0,0 +1,8 @@
+{application, fe,
+ [{description, "ERICSSON NR FOR FE"},
+ {vsn, "2.1.1"},
+ {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {registered, []},
+ {applications, []},
+ {env, []},
+ {start, {fe2, start, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app
new file mode 100644
index 0000000000..47ea248720
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app
@@ -0,0 +1,8 @@
+{application, fe,
+ [{description, "ERICSSON NR FOR FE"},
+ {vsn, "2.1"},
+ {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {registered, []},
+ {applications, []},
+ {env, []},
+ {start, {fe2, start, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app
new file mode 100644
index 0000000000..0696e2494c
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app
@@ -0,0 +1,7 @@
+{application, fe,
+ [{description, "ERICSSON NR FOR FE"},
+ {vsn, "3.1"},
+ {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {registered, []},
+ {applications, []},
+ {mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.appup
new file mode 100644
index 0000000000..6b99c47e53
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.appup
@@ -0,0 +1,28 @@
+%% -*- erlang -*-
+%% Release upgrade script for fe (front end)
+%%
+
+{
+ "3.1",
+ %% Upgrade from:
+ [
+ {<<"2\\.[0-9]+">>, % matches 2.X in full length and 2.X.Y... only partly
+ [{update, fe1, soft, soft_purge, soft_purge, []},
+ {update, fe2, soft, soft_purge, soft_purge, [fe1]},
+ {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]}
+ ]},
+ {<<"2(\\.[0-9]+)+">>, % matches 2.X.Y... in full length
+ [{update, fe1, soft, soft_purge, soft_purge, []},
+ {update, fe2, soft, soft_purge, soft_purge, [fe1]},
+ {update, fe3, {advanced, extra}, soft_purge, soft_purge,[fe1, fe2]},
+ restart_emulator]}
+ ],
+
+ %% Downgrade to:
+ [
+ {<<"2\\.[0-9]+">>, % matches 2.X in full length and 2.X.Y... only partly
+ [{update, fe2, soft, soft_purge, soft_purge, []},
+ {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]}
+ ]}
+ ]
+}.
diff --git a/lib/sasl/test/systools_SUITE_data/lib/sasl-9.9/ebin/sasl.app b/lib/sasl/test/systools_SUITE_data/lib/sasl-9.9/ebin/sasl.app
new file mode 100644
index 0000000000..3bcc1a4619
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/lib/sasl-9.9/ebin/sasl.app
@@ -0,0 +1,6 @@
+{application, sasl,
+ [{description, "FAKE FUTURE SASL"},
+ {vsn, "9.9"},
+ {modules, []},
+ {registered, []},
+ {applications, []}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/lib/sasl-9.9/ebin/sasl.appup b/lib/sasl/test/systools_SUITE_data/lib/sasl-9.9/ebin/sasl.appup
new file mode 100644
index 0000000000..cff0c69b6e
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/lib/sasl-9.9/ebin/sasl.appup
@@ -0,0 +1,12 @@
+%%
+%% Fake release upgrade script for sasl
+%%
+
+{
+ "9.9",
+ [{<<".+">>,[restart_new_emulator]}
+ ],
+
+ [{<<".+">>,[restart_new_emulator]}
+ ]
+}.
diff --git a/lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.app b/lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.app
new file mode 100644
index 0000000000..aaeb37fa4d
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.app
@@ -0,0 +1,6 @@
+{application, sasl,
+ [{description, "FAKE SASL"},
+ {vsn, "1.0"},
+ {modules, []},
+ {registered, []},
+ {applications, []}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.appup b/lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.appup
new file mode 100644
index 0000000000..796a1e7368
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.appup
@@ -0,0 +1,12 @@
+%%
+%% Fake release upgrade script for sasl
+%%
+
+{
+ "1.0",
+ [
+ ],
+
+ [
+ ]
+}.
diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl
index bb93f38fa7..2ab9e269f9 100644
--- a/lib/sasl/test/systools_rc_SUITE.erl
+++ b/lib/sasl/test/systools_rc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,14 +21,15 @@
-include_lib("test_server/include/test_server.hrl").
-include_lib("sasl/src/systools.hrl").
-export([all/0,groups/0,init_per_group/2,end_per_group/2,
- syntax_check/1, translate/1, translate_app/1]).
+ syntax_check/1, translate/1, translate_app/1,
+ translate_emulator_restarts/1]).
%%-----------------------------------------------------------------
%% erl -compile systools_rc_SUITE @i ../src/ @i ../../test_server/include/
%% c(systools_rc_SUITE, [{i, "../src"}, {i, "../../test_server/include"}]).
%%-----------------------------------------------------------------
all() ->
- [syntax_check, translate, translate_app].
+ [syntax_check, translate, translate_app, translate_emulator_restarts].
groups() ->
[].
@@ -87,7 +88,8 @@ syntax_check(Config) when is_list(Config) ->
{sync_nodes, id1, {m, f, [a]}},
{sync_nodes, id2, [cp1, cp2]},
{apply, {m,f,[a]}},
- restart_new_emulator
+ restart_new_emulator,
+ restart_emulator
],
?line {ok, _} = systools_rc:translate_scripts([S2], Apps, []),
S3 = [{apply, {m, f, a}}],
@@ -486,3 +488,85 @@ io:format("X2=~p~n", [X2]),
{purge,[pelle,kalle]},
{apply,{application,unload,[pelle]}}] = X3,
?line ok.
+
+
+translate_emulator_restarts(_Config) ->
+ Apps =
+ [#application{name = test,
+ description = "TEST",
+ vsn = "1.0",
+ modules = [{foo,1},{bar,1},{baz,1}],
+ regs = [],
+ mod = {sasl, []}},
+ #application{name = test,
+ description = "TEST2",
+ vsn = "1.0",
+ modules = [{x,1},{y,1},{z,1}],
+ regs = [],
+ mod = {sasl, []}}],
+ %% restart_new_emulator
+ Up1 = [{update, foo, soft, soft_purge, soft_purge, []},restart_new_emulator],
+ ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []),
+ ?line [restart_new_emulator,
+ {load_object_code, {test,"1.0",[foo]}},
+ point_of_no_return,
+ {suspend,[foo]},
+ {load,{foo,soft_purge,soft_purge}},
+ {resume,[foo]}] = X1,
+
+ %% restart_emulator
+ Up2 = [{update, foo, soft, soft_purge, soft_purge, []},restart_emulator],
+ ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []),
+ ?line [{load_object_code, {test,"1.0",[foo]}},
+ point_of_no_return,
+ {suspend,[foo]},
+ {load,{foo,soft_purge,soft_purge}},
+ {resume,[foo]},
+ restart_emulator] = X2,
+
+ %% restart_emulator + restart_new_emulator
+ Up3 = [{update, foo, soft, soft_purge, soft_purge, []},
+ restart_emulator,
+ restart_new_emulator],
+ ?line {ok, X3} = systools_rc:translate_scripts([Up3], Apps, []),
+ ?line [restart_new_emulator,
+ {load_object_code, {test,"1.0",[foo]}},
+ point_of_no_return,
+ {suspend,[foo]},
+ {load,{foo,soft_purge,soft_purge}},
+ {resume,[foo]},
+ restart_emulator] = X3,
+
+ %% restart_emulator + restart_new_emulator
+ Up4a = [{update, foo, soft, soft_purge, soft_purge, []},
+ restart_emulator,
+ restart_new_emulator],
+ Up4b = [restart_new_emulator,
+ {update, x, soft, soft_purge, soft_purge, []},
+ restart_emulator,
+ restart_emulator],
+ ?line {ok, X4} = systools_rc:translate_scripts([Up4a,Up4b], Apps, []),
+ ?line [restart_new_emulator,
+ {load_object_code, {test,"1.0",[foo,x]}},
+ point_of_no_return,
+ {suspend,[foo]},
+ {load,{foo,soft_purge,soft_purge}},
+ {resume,[foo]},
+ {suspend,[x]},
+ {load,{x,soft_purge,soft_purge}},
+ {resume,[x]},
+ restart_emulator] = X4,
+
+ %% only restart_new_emulator
+ Up5 = [restart_new_emulator],
+ ?line {ok, X5} = systools_rc:translate_scripts([Up5], Apps, []),
+ ?line [restart_new_emulator,
+ point_of_no_return] = X5,
+
+ %% only restart_emulator
+ Up6 = [restart_emulator],
+ ?line {ok, X6} = systools_rc:translate_scripts([Up6], Apps, []),
+ ?line [point_of_no_return,
+ restart_emulator] = X6,
+
+ ok.
diff --git a/lib/sasl/test/test_lib.hrl b/lib/sasl/test/test_lib.hrl
new file mode 100644
index 0000000000..eeef721647
--- /dev/null
+++ b/lib/sasl/test/test_lib.hrl
@@ -0,0 +1,3 @@
+-define(ertsvsn,"4.4").
+-define(kernelvsn,"2.14.3").
+-define(stdlibvsn,"1.17.3").
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index 2db134af48..23694f1399 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 2.1.10
+SASL_VSN = 2.2
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 54a63833e6..94e81188b5 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -1 +1,27 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+{"%VSN%",
+ %% Up from - max two major revisions back
+ [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ %% Down to - max two major revisions back
+ [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+}.
diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl
index 0cca030b3d..8a2cb5ea6b 100644
--- a/lib/stdlib/test/stdlib_SUITE.erl
+++ b/lib/stdlib/test/stdlib_SUITE.erl
@@ -33,8 +33,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
--export([app_test/1]).
--define(cases, [app_test]).
+-export([app_test/1, appup_test/1]).
%%
%% all/1
@@ -42,7 +41,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test].
+ [app_test, appup_test].
groups() ->
[].
@@ -79,3 +78,61 @@ app_test(Config) when is_list(Config) ->
?t:app_test(stdlib),
ok.
+%% Test that appup allows upgrade from/downgrade to a maximum of two
+%% major releases back.
+appup_test(_Config) ->
+ application:load(stdlib),
+ {_,_,Vsn} = lists:keyfind(stdlib,1,application:loaded_applications()),
+ AppupFile = filename:join([code:lib_dir(stdlib),ebin,"stdlib.appup"]),
+ {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
+ ct:log("~p~n",[AppupScript]),
+ {OkVsns,NokVsns} = create_test_vsns(Vsn),
+ check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
+ check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
+ check_appup(NokVsns,UpFrom,error),
+ check_appup(NokVsns,DownTo,error),
+ ok.
+
+create_test_vsns(Current) ->
+ [XStr,YStr|Rest] = string:tokens(Current,"."),
+ X = list_to_integer(XStr),
+ Y = list_to_integer(YStr),
+ SecondMajor = vsn(X,Y-2),
+ SecondMinor = SecondMajor ++ ".1.3",
+ FirstMajor = vsn(X,Y-1),
+ FirstMinor = FirstMajor ++ ".57",
+ ThisMajor = vsn(X,Y),
+ This =
+ case Rest of
+ [] ->
+ [];
+ ["1"] ->
+ [ThisMajor];
+ _ ->
+ ThisMinor = ThisMajor ++ ".1",
+ [ThisMajor,ThisMinor]
+ end,
+ OkVsns = This ++ [FirstMajor, FirstMinor, SecondMajor, SecondMinor],
+
+ ThirdMajor = vsn(X,Y-3),
+ ThirdMinor = ThirdMajor ++ ".10.12",
+ Illegal = ThisMajor ++ ",1",
+ Newer1Major = vsn(X,Y+1),
+ Newer1Minor = Newer1Major ++ ".1",
+ Newer2Major = ThisMajor ++ "1",
+ NokVsns = [ThirdMajor,ThirdMinor,
+ Illegal,
+ Newer1Major,Newer1Minor,
+ Newer2Major],
+ {OkVsns,NokVsns}.
+
+vsn(X,Y) ->
+ integer_to_list(X) ++ "." ++ integer_to_list(Y).
+
+check_appup([Vsn|Vsns],Instrs,Expected) ->
+ case systools_relup:appup_search_for_version(Vsn, Instrs) of
+ Expected -> check_appup(Vsns,Instrs,Expected);
+ Other -> ct:fail({unexpected_result_for_vsn,Vsn,Other})
+ end;
+check_appup([],_,_) ->
+ ok.
diff --git a/system/doc/design_principles/appup_cookbook.xml b/system/doc/design_principles/appup_cookbook.xml
index bc61578953..798b23d847 100644
--- a/system/doc/design_principles/appup_cookbook.xml
+++ b/system/doc/design_principles/appup_cookbook.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -603,25 +603,60 @@ code_change(_OldVsn, State, port) ->
</section>
<section>
- <title>Emulator Restart</title>
- <p>If the emulator can or should be restarted, the very simple
- <c>.relup</c> file can be created manually:</p>
+ <title>Emulator Restart and Upgrade</title>
+ <p>There are two upgrade instructions that will restart the emulator:</p>
+ <taglist>
+ <tag><c>restart_new_emulator</c></tag>
+ <item>Intended for when erts, kernel, stdlib or sasl is
+ upgraded. It is automatically added when the relup file is
+ generated by <c>systools:make_relup/3,4</c>. It is executed
+ before all other upgrade instructions. See
+ <seealso marker="release_handling#restart_new_emulator_instr">Release
+ Handling</seealso> for more information about this
+ instruction.</item>
+ <tag><c>restart_emulator</c></tag>
+ <item>Used when a restart of the emulator is required after all
+ other upgrade instructions are executed. See
+ <seealso marker="release_handling#restart_emulator_instr">Release
+ Handling</seealso> for more information about this
+ instruction.</item>
+ </taglist>
+
+ <p>If an emulator restart is necessary and no upgrade instructions
+ are needed, i.e. if the restart itself is enough for the
+ upgraded applications to start running the new versions, a very
+ simple <c>.relup</c> file can be created manually:</p>
<code type="none">
{"B",
[{"A",
[],
- [restart_new_emulator]}],
+ [restart_emulator]}],
[{"A",
[],
- [restart_new_emulator]}]
+ [restart_emulator]}]
}.</code>
- <p>This way, the release handler framework with automatic packing
- and unpacking of release packages, automatic path updates etc. can
- be used without having to specify <c>.appup</c> files.</p>
- <p>If some transformation of persistent data, for example database
- contents, needs to be done before installing the new release
- version, instructions for this can be added to the <c>.relup</c>
- file as well.</p>
+ <p>In this case, the release handler framework with automatic
+ packing and unpacking of release packages, automatic path
+ updates etc. can be used without having to specify <c>.appup</c>
+ files.</p>
+ </section>
+
+ <section>
+ <title>Emulator Upgrade from pre OTP R15</title>
+ <p>From OTP R15, an emulator upgrade is performed by restarting
+ the emulator with new versions of the core applications
+ (<c>kernel</c>, <c>stdlib</c> and <c>sasl</c>) before loading code
+ and running upgrade instruction for other applications. For this
+ to work, the release to upgrade from must includes OTP R15 or
+ later. For the case where the release to upgrade from includes an
+ earlier emulator version, <c>systools:make_relup</c> will create a
+ backwards compatible relup file. This means that all upgrade
+ instructions will be executed before the emulator is
+ restarted. The new application code will therefore be loaded into
+ the old emulator. If the new code is compiled with the new
+ emulator, there might be cases where the beam format has changed
+ and beam files can not be loaded. To overcome this problem, the
+ new code should be compiled with the old emulator.</p>
</section>
</chapter>
diff --git a/system/doc/design_principles/release_handling.xml b/system/doc/design_principles/release_handling.xml
index 1d62c242c0..8ed36f3c56 100644
--- a/system/doc/design_principles/release_handling.xml
+++ b/system/doc/design_principles/release_handling.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -313,18 +313,35 @@
</section>
<section>
+ <marker id="restart_new_emulator_instr"></marker>
<title>restart_new_emulator (low-level)</title>
<p>This instruction is used when changing to a new emulator
- version, or if a system reboot is needed for some other reason.
- Requires that the system is started with heart beat
+ version, or when any of the core applications kernel, stdlib
+ or sasl is upgraded. If a system reboot is needed for some
+ other reason, the <c>restart_emulator</c> instruction should
+ be used instead.</p>
+ <p>Requires that the system is started with heart beat
monitoring, see <c>erl(1)</c> and <c>heart(3)</c>.</p>
- <p>When the release handler encounters the instruction, it shuts
- down the current emulator by calling <c>init:reboot()</c>, see
+ <p>The <c>restart_new_emulator</c> instruction shall always be
+ the very first instruction in a relup. If the relup is
+ generated by <c>systools:make_relup/3,4</c> this is
+ automatically ensured.</p>
+ <p>When the release handler encounters the instruction, it first
+ generates a temporary boot file, which starts the new versions
+ of the emulator and the core applications. Then it shuts down
+ the current emulator by calling <c>init:reboot()</c>, see
<c>init(3)</c>. All processes are terminated gracefully and
the system can then be rebooted by the heart program, using
- the new release version. This new version must still be made
- permanent when the new emulator is up and running. Otherwise,
- the old version is used in case of a new system reboot.</p>
+ the temporary boot file. After the reboot, the rest of the
+ relup instructions are executed. This is done as a part of the
+ boot script.</p>
+ <p>An info report is written when the upgrade is completed. To
+ programatically find out if the upgrade is complete,
+ call <c>release_handler:which_releases/0</c> and check if the
+ expected release has status <c>current</c>.</p>
+ <p>The new version must be made permanent when the new emulator
+ is up and running. Otherwise, the old version will be used in
+ case of a new system reboot.</p>
<p>On UNIX, the release handler tells the heart program which
command to use to reboot the system. Note that the environment
variable <c>HEART_COMMAND</c>, normally used by the heart
@@ -333,6 +350,25 @@
by using the SASL configuration parameter <c>start_prg</c>, see
<c>sasl(6)</c>.</p>
</section>
+
+ <section>
+ <marker id="restart_emulator_instr"></marker>
+ <title>restart_emulator (low-level)</title>
+ <p>This instruction is not related to upgrades of erts or any of
+ the core applications. It can be used by any application to
+ force a restart of the emulator after all upgrade instructions
+ are executed.</p>
+ <p>There can only be one <c>restart_emulator</c> instruction in
+ a relup script, and it shall always be placed at the end. If
+ the relup is generated by <c>systools:make_relup/3,4</c> this
+ is automatically ensured.</p>
+ <p>When the release handler encounters the instruction, it shuts down
+ the emulator by calling <c>init:reboot()</c>, see
+ <c>init(3)</c>. All processes are terminated gracefully and
+ the system can then be rebooted by the heart program using the
+ new release version. No more upgrade instruction will be
+ executed after the restart.</p>
+ </section>
</section>
<section>