diff options
Diffstat (limited to 'lib/sasl')
-rw-r--r-- | lib/sasl/src/release_handler.erl | 288 | ||||
-rw-r--r-- | lib/sasl/src/release_handler_1.erl | 34 | ||||
-rw-r--r-- | lib/sasl/src/systools_make.erl | 114 | ||||
-rw-r--r-- | lib/sasl/src/systools_rc.erl | 25 | ||||
-rw-r--r-- | lib/sasl/src/systools_relup.erl | 17 | ||||
-rw-r--r-- | lib/sasl/test/Makefile | 4 | ||||
-rw-r--r-- | lib/sasl/test/installer.erl | 562 | ||||
-rw-r--r-- | lib/sasl/test/release_handler_SUITE.erl | 318 | ||||
-rw-r--r-- | lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup | 2 | ||||
-rw-r--r-- | lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl | 4 | ||||
-rw-r--r-- | lib/sasl/test/systools_SUITE.erl | 271 | ||||
-rw-r--r-- | lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.app | 6 | ||||
-rw-r--r-- | lib/sasl/test/systools_SUITE_data/lib/sasl/ebin/sasl.appup | 12 | ||||
-rw-r--r-- | lib/sasl/test/test_lib.hrl | 3 |
14 files changed, 1352 insertions, 308 deletions
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl index bc08f94dff..38aee9ecde 100644 --- a/lib/sasl/src/release_handler.erl +++ b/lib/sasl/src/release_handler.erl @@ -26,7 +26,8 @@ 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, + install_release/1, install_release/2, + new_emulator_upgrade/2, remove_release/1, which_releases/0, 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, @@ -40,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, @@ -61,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 - %%----------------------------------------------------------------- @@ -74,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 @@ -230,6 +240,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). @@ -578,7 +603,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 = @@ -597,6 +622,10 @@ handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) -> gen_server:reply(From, {ok, CurrentVsn, Descr}), init:reboot(), {noreply, NS}; + {restart_new_emulator_then_continue, 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, " @@ -941,7 +970,36 @@ 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_then_continue, 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, + RelDir,Releases,Masters), + + %% Then execute the relup script mon_nodes(true), EnvBefore = application_controller:prep_config_change(), Apps = change_appl_data(RelDir, Release, Masters), @@ -949,30 +1007,18 @@ 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}; + NReleases1 = set_status(Vsn, current, NReleases), + {ok, NReleases1, Unpurged, CurrentVsn, Descr}; restart_new_emulator when Static == true -> throw(static_emulator); restart_new_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), + RelDir, Vsn, Release, + NReleases, Masters), {restart_new_emulator, CurrentVsn, Descr}; Else -> application_controller:config_change(EnvBefore), @@ -986,6 +1032,68 @@ 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_boot_hybrid(CurrentVsn,ToVsn,TmpVsn,BaseLibs, + RelDir,Opts,Masters), + SysConfig = filename:join([RelDir,CurrentVsn,"sys.config"]), + copy_file(SysConfig,filename:join(RelDir,TmpVsn),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_boot_hybrid(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_boot_hybrid(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_rm_tmp_release(?tmp_vsn(_)=TmpVsn,RelDir,Releases,Masters) -> + remove_dir(filename:join(RelDir,TmpVsn),Masters), + lists:keydelete(TmpVsn,#release.vsn,Releases); +new_emulator_rm_tmp_release(_,_,Releases,_) -> + Releases. + + %%% 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 @@ -1260,21 +1368,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. @@ -1487,6 +1605,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, @@ -1512,19 +1639,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 @@ -1538,27 +1656,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 @@ -1617,12 +1749,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). @@ -1844,6 +1989,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..4a776fd18b 100644 --- a/lib/sasl/src/release_handler_1.erl +++ b/lib/sasl/src/release_handler_1.erl @@ -47,26 +47,34 @@ %%%----------------------------------------------------------------- %%% 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) -> + {ok,PurgeMods}; + {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, [], [], [], []). diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index 7f400f5cce..42b528540e 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_boot_hybrid/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_boot_hybrid(TmpVsn, Boot1, Boot2, Paths, Args) -> + catch do_make_boot_hybrid(TmpVsn, Boot1, Boot2, Paths, Args). +do_make_boot_hybrid(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 | diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl index daadb79967..6f01901fbc 100644 --- a/lib/sasl/src/systools_rc.erl +++ b/lib/sasl/src/systools_rc.erl @@ -144,7 +144,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_restart_new_emulator(Mode,Before3,After2), + NewScript = Before4 ++ [point_of_no_return | After4], + check_syntax(NewScript), {ok, NewScript}. @@ -699,6 +702,25 @@ mlo([{load_object_code, {Lib, LibVsn, Mods}} | T]) -> mlo([]) -> []. %%----------------------------------------------------------------- +%% RESTART DIFF EMULATOR +%% ----------------------------------------------------------------- +%% ----------------------------------------------------------------- +%% Check if a diff_vsn_restart_new_emulator instruction exists (i.e. if the +%% emulator version is changed). If so, this must be done first for +%% upgrade and last for downgrade. +%% ----------------------------------------------------------------- +sort_restart_new_emulator(Mode,Before,After) -> + case lists:delete(diff_vsn_restart_new_emulator,After) of + After -> + {Before,After}; + NewAfter when Mode==up -> + {[restart_new_emulator|Before],NewAfter}; + NewAfter when Mode==dn -> + {Before,NewAfter++[restart_new_emulator]} + end. + + +%%----------------------------------------------------------------- %% SYNTAX CHECK %%----------------------------------------------------------------- %%----------------------------------------------------------------- @@ -817,6 +839,7 @@ check_op({apply, {M, F, A}}) -> check_func(F), check_args(A); check_op(restart_new_emulator) -> ok; +check_op(diff_vsn_restart_new_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..682f5f5cc9 100644 --- a/lib/sasl/src/systools_relup.erl +++ b/lib/sasl/src/systools_relup.erl @@ -263,7 +263,7 @@ foreach_baserel_up(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts, {RUs3, Ws3} = create_remove_app_scripts(BaseRel, TopRel, RUs2, Ws2), - {RUs4, Ws4} = + {RUs4, Ws4} = check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts), BaseApps = @@ -343,9 +343,20 @@ 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 -> + %% The diff_vsn_restart_new_emulator instruction will be replaced + %% by a restart_new_emulator instruction in systools_rc, and + %% placed in the proper order according to mode (up or dn). + %% We will also allow an extra restart of emulator (specified by + %% the restart_emulator option) at the end of the upgrade, for + %% application specific purposes. + NewRUs = [[diff_vsn_restart_new_emulator]|RUs], + NewWs = [{erts_vsn_changed, {N1, N2}} | 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}; _ -> {RUs, Ws} 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..d77b0e8538 100644 --- a/lib/sasl/test/installer.erl +++ b/lib/sasl/test/installer.erl @@ -19,20 +19,32 @@ -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_3/1]). -export([client1_1/4]). -export([client2/3]). -export([stop/1]). @@ -46,28 +58,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,172 +100,337 @@ 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 + {"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_2a(TestNode) -> + ?print(["upgrade_restart_2a start"]), + + {ok,"P1G",[old_emu,rm_appl]} = release_handler:install_release("P1G"), + ?print(["upgrade_restart_2a 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. + + %%----------------------------------------------------------------- @@ -272,6 +456,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 +486,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 +510,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 +529,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 +549,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 +562,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 +578,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 +594,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 +606,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 +744,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 +769,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 +816,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 +864,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 +898,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 +999,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 +1027,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 +1053,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 af2183bfff..c2930b1039 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. @@ -69,6 +71,7 @@ groups() -> {release_single,[], [ upgrade, + upgrade_restart, client1, client2 ]}, @@ -82,7 +85,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); @@ -95,6 +98,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); @@ -155,7 +159,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) -> @@ -191,7 +195,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) -> @@ -236,7 +243,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]), @@ -259,54 +266,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. @@ -323,6 +351,46 @@ 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 + TestNodeInit2 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, upgrade_restart_2, []), + stop_cover(TestNode), + ok = rpc_inst(TestNode, upgrade_restart_2a, []), + wait_nodes_up([{TestNode,TestNodeInit2}],"upgrade_restart_2",[]), + + %% 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 @@ -1147,11 +1215,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]), @@ -1179,11 +1246,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)), @@ -1205,6 +1273,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. @@ -1456,7 +1525,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(), @@ -1466,7 +1535,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]), @@ -1474,7 +1543,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, @@ -1483,22 +1552,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 @@ -1515,13 +1584,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 -> @@ -1531,7 +1600,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) -> @@ -1657,9 +1726,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 ++ "*"), @@ -1694,14 +1771,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), @@ -1745,17 +1830,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 @@ -1763,60 +1846,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), @@ -1824,7 +1945,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 -> []; @@ -1841,18 +1962,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}, @@ -2033,7 +2155,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), @@ -2158,7 +2280,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}) -> @@ -2168,7 +2289,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, @@ -2217,3 +2338,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/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl index e352247d44..a3e97c4543 100644 --- a/lib/sasl/test/systools_SUITE.erl +++ b/lib/sasl/test/systools_SUITE.erl @@ -51,8 +51,8 @@ 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([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,7 +67,7 @@ suite() -> [{ct_hooks, [ts_install_cth]}]. all() -> - [{group, script}, {group, tar}, {group, relup}, + [{group, script}, {group, tar}, {group, relup}, {group, hybrid}, {group, tickets}]. groups() -> @@ -85,6 +85,7 @@ groups() -> {relup, [], [normal_relup, abnormal_relup, no_appup_relup, bad_appup_relup, app_start_type_relup]}, + {hybrid, [], [normal_hybrid,hybrid_no_old_sasl,hybrid_no_new_sasl]}, {tickets, [], [otp_6226]}]. init_per_group(_GroupName, Config) -> @@ -1143,6 +1144,8 @@ normal_relup(Config) when is_list(Config) -> ?line {LatestDir,LatestName} = create_script(latest0,Config), ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), ?line {_LatestDir2,LatestName2} = create_script(latest2,Config), + ?line {_LatestDir1CurrErts,LatestName1CurrErts} = + create_script(latest1_current_erts,Config), ?line DataDir = filename:absname(?copydir), ?line LibDir = [fname([DataDir, d_normal, lib])], @@ -1160,6 +1163,31 @@ normal_relup(Config) when is_list(Config) -> ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), ?line ok = check_restart_emulator(), + %% Check that new erts version generates a restart_new_emulator + %% instruction + %% (One erts_vsn_changed warning for upgrade and one for downgrade) + ?line {ok, _ , _, [{erts_vsn_changed,_},{erts_vsn_changed,_}]} = + systools:make_relup(LatestName, + [LatestName1CurrErts], + [LatestName1CurrErts], + [{path, P},silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line ok = check_restart_emulator_diff_erts(), + + + %% 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(LatestName, + [LatestName1CurrErts], + [LatestName1CurrErts], + [{path, P},restart_emulator,silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line ok = check_restart_emulator(), + ?line ok = check_restart_emulator_diff_erts(), + + %% This is the ultra normal case ?line ok = systools:make_relup(LatestName, [LatestName1], [LatestName1], [{path, P}]), @@ -1222,6 +1250,12 @@ check_restart_emulator() -> restart_new_emulator = lists:last(Dn), ok. +check_restart_emulator_diff_erts() -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + [restart_new_emulator|_] = Up, + restart_new_emulator = lists:last(Dn), + ok. + %% make_relup %% no_appup_relup(suite) -> []; @@ -1371,6 +1405,168 @@ app_start_type_relup(Config) when is_list(Config) -> 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_sasl,Config), + ?line {_Dir2,Name2} = create_script(current_all_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, _ , []} = 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_boot_hybrid("tmp_vsn",Boot1,Boot2, + BasePaths, [dummy,args]), + + ?line {script,{"Test release 4","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_boot_hybrid 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,Config), + ?line {_Dir2,Name2} = create_script(current_all_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, _ , []} = 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_boot_hybrid("tmp_vsn",Boot1,Boot2, + BasePaths,[dummy,args]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% Check that systools_make:make_boot_hybrid 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_sasl,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, _ , []} = 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_boot_hybrid("tmp_vsn",Boot1,Boot2, + BasePaths,[dummy,args]), + + ?line ok = file:set_cwd(OldDir), + ok. + + + otp_6226(suite) -> []; otp_6226(doc) -> @@ -1709,6 +1905,36 @@ create_script(latest1,Config) -> []), ?line ok = file:close(Fd), {filename:dirname(Name), filename:basename(Name)}; +create_script(latest1_sasl,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_sasl), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release 2", "LATEST1_SASL"}, + {erts,"4.4"}, + [{kernel,"1.0"}, + {stdlib,"1.0"}, + {sasl,"1.0"}, + {db,"1.0"}, + {fe,"3.1"}]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest1_current_erts,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest_current_erts'), + ?line ErtsVer = erlang:system_info(version), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release 2", "LATEST1_CURRENT_ERTS"}, + {erts,ErtsVer}, + [{kernel,"1.0"}, % fake - will never happen for real that + {stdlib,"1.0"}, % erts changes verson but not kernel and stdlib. + {db,"1.0"}, + {fe,"3.1"}]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; create_script(latest2,Config) -> ?line PrivDir = ?privdir, ?line Name = fname(PrivDir, 'latest-2'), @@ -1813,8 +2039,47 @@ create_script(latest_app_start_type2,Config) -> {stdlib,StdlibVer} | OtherAppsRel]}, ?line io:format(Fd,"~p.~n",[RelfileContent]), ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(current_all_no_sasl,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'current_all_no_sasl'), + ?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 4", "CURRENT_ALL_NO_SASL"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer}, + {db,"2.1"}, + {fe,"3.1"}]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(current_all_sasl,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'current_all_sasl'), + ?line ErtsVer = erlang:system_info(version), + ?line application:load(sasl), + ?line Apps = application_controller:loaded_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {value,{_,_,SaslVer}} = lists:keysearch(sasl,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release 4", "CURRENT_ALL_SASL"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer}, + {sasl,SaslVer}, + {db,"2.1"}]},% fe left out here on purpose - for normal_hybrid test + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), {filename:dirname(Name), filename:basename(Name)}. + create_include_files(inc1, Config) -> ?line PrivDir = ?privdir, ?line Name = fname(PrivDir, inc1), 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/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"). |