From 91c46de4aa3d3a910033f91803148a614749964a Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Sat, 26 Feb 2022 20:33:42 +0800 Subject: [PATCH 01/13] fix(relup): inject relup instructions to the end of relup file --- rebar.config.erl | 3 +- scripts/inject-relup.escript | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100755 scripts/inject-relup.escript diff --git a/rebar.config.erl b/rebar.config.erl index ce8933570..11db1abb1 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -129,7 +129,8 @@ prod_overrides() -> [{add, [ {erl_opts, [deterministic]}]}]. relup_deps(Profile) -> - {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", compile, "scripts/inject-deps.escript " ++ atom_to_list(Profile)}]}. + {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", release, + "scripts/inject-relup.escript " ++ filename:join(["_build", Profile, "rel", "emqx"])}]}. profiles() -> Vsn = get_vsn(), diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript new file mode 100755 index 000000000..a7ba23689 --- /dev/null +++ b/scripts/inject-relup.escript @@ -0,0 +1,58 @@ +#!/usr/bin/env escript + +%% This script injects implicit relup instructions for emqx applications. + +-mode(compile). + +-define(ERROR(FORMAT, ARGS), io:format(standard_error, "[inject-relup] " ++ FORMAT ++"~n", ARGS)). +-define(INFO(FORMAT, ARGS), io:format(user, "[inject-relup] " ++ FORMAT ++"~n", ARGS)). + +usage() -> + "Usage: " ++ escript:script_name() ++ " ". + +main([Dir]) -> + case filelib:is_dir(Dir) of + true -> + ok = inject(Dir); + false -> + ?ERROR("not a valid dir: ~p", [Dir]), + erlang:halt(1) + end; +main(_Args) -> + ?ERROR("~s", [usage()]), + erlang:halt(1). + +inject(Dir) -> + RelupFiles = filelib:wildcard(filename:join([Dir, "releases", "*", "relup"])), + lists:foreach(fun(File) -> + case file:consult(File) of + {ok, [{CurrRelVsn, UpVsnRUs, DnVsnRUs}]} -> + ?INFO("injecting instructions to: ~p", [File]), + UpdatedContent = [{CurrRelVsn, inject_relup_instrs(CurrRelVsn, UpVsnRUs), + inject_relup_instrs(CurrRelVsn, DnVsnRUs)}], + file:write_file("/tmp/" ++ filename:basename(File), term_to_text(UpdatedContent)); + {ok, _BadFormat} -> + ?ERROR("bad formatted relup file: ~p", [File]); + {error, Reason} -> + ?ERROR("read relup file ~p failed: ~p", [File, Reason]) + end + end, RelupFiles). + +inject_relup_instrs(CurrRelVsn, RUs) -> + [{Vsn, Desc, append_emqx_relup_instrs(CurrRelVsn, Vsn, Instrs)} || {Vsn, Desc, Instrs} <- RUs]. + +%% The `{apply, emqx_relup, post_release_upgrade, []}` will be appended to the end of +%% the instruction lists. +append_emqx_relup_instrs(CurrRelVsn, Vsn, Instrs) -> + case lists:reverse(Instrs) of + [{apply, {emqx_relup, post_release_upgrade, _}} | _] -> + Instrs; + RInstrs -> + lists:reverse([instr_post_release_upgrade(CurrRelVsn, Vsn) | RInstrs]) + end. + +instr_post_release_upgrade(CurrRelVsn, Vsn) -> + {apply, {emqx_relup, post_release_upgrade, [CurrRelVsn, Vsn]}}. + +term_to_text(Term) -> + io_lib:format("~p.", [Term]). \ No newline at end of file From 692d1e2a3371f633099d6bee2c06558e7f12ef22 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Sat, 26 Feb 2022 21:31:09 +0800 Subject: [PATCH 02/13] fix(relup): OTP_VSN: unbound variable --- scripts/relup-base-packages.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/relup-base-packages.sh b/scripts/relup-base-packages.sh index f86185e0a..2a47b5135 100755 --- a/scripts/relup-base-packages.sh +++ b/scripts/relup-base-packages.sh @@ -28,6 +28,7 @@ case $PROFILE in esac SYSTEM="${SYSTEM:-$(./scripts/get-distro.sh)}" +OTP_VSN="${OTP_VSN:-$(./scripts/get-otp-vsn.sh)}" ARCH="${ARCH:-$(uname -m)}" case "$ARCH" in From 2365d1e983d0d9c56c6de31bb94f04ef3ce31475 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Sun, 27 Feb 2022 09:28:35 +0800 Subject: [PATCH 03/13] fix(relup): inject relup only for the current rel vsn --- rebar.config.erl | 17 +++++---- scripts/inject-relup.escript | 68 ++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/rebar.config.erl b/rebar.config.erl index 11db1abb1..447e58f97 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -128,31 +128,34 @@ prod_compile_opts() -> prod_overrides() -> [{add, [ {erl_opts, [deterministic]}]}]. -relup_deps(Profile) -> - {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", release, - "scripts/inject-relup.escript " ++ filename:join(["_build", Profile, "rel", "emqx"])}]}. +relup_deps(Profile, Vsn) -> + InjectCmd = "scripts/inject-relup.escript " ++ filename:join(["_build", Profile, "rel", "emqx", "releases", Vsn]), + {post_hooks, + [ {"(linux|darwin|solaris|freebsd|netbsd|openbsd)", relup, InjectCmd} + ] + }. profiles() -> Vsn = get_vsn(), [ {'emqx', [ {erl_opts, prod_compile_opts()} , {relx, relx(Vsn, cloud, bin)} , {overrides, prod_overrides()} - , relup_deps('emqx') + , relup_deps('emqx', Vsn) ]} , {'emqx-pkg', [ {erl_opts, prod_compile_opts()} , {relx, relx(Vsn, cloud, pkg)} , {overrides, prod_overrides()} - , relup_deps('emqx-pkg') + , relup_deps('emqx-pkg', Vsn) ]} , {'emqx-edge', [ {erl_opts, prod_compile_opts()} , {relx, relx(Vsn, edge, bin)} , {overrides, prod_overrides()} - , relup_deps('emqx-edge') + , relup_deps('emqx-edge', Vsn) ]} , {'emqx-edge-pkg', [ {erl_opts, prod_compile_opts()} , {relx, relx(Vsn, edge, pkg)} , {overrides, prod_overrides()} - , relup_deps('emqx-edge-pkg') + , relup_deps('emqx-edge-pkg', Vsn) ]} , {check, [ {erl_opts, common_compile_opts()} ]} diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript index a7ba23689..86c541ada 100755 --- a/scripts/inject-relup.escript +++ b/scripts/inject-relup.escript @@ -8,51 +8,59 @@ -define(INFO(FORMAT, ARGS), io:format(user, "[inject-relup] " ++ FORMAT ++"~n", ARGS)). usage() -> - "Usage: " ++ escript:script_name() ++ " ". + "Usage: " ++ escript:script_name() ++ " ". -main([Dir]) -> - case filelib:is_dir(Dir) of - true -> - ok = inject(Dir); +main([DirOrFile]) -> + case filelib:is_dir(DirOrFile) of + true -> ok = inject_dir(DirOrFile); false -> - ?ERROR("not a valid dir: ~p", [Dir]), - erlang:halt(1) + case filelib:is_regular(DirOrFile) of + true -> inject_file(DirOrFile); + false -> + ?ERROR("not a valid file: ~p", [DirOrFile]), + erlang:halt(1) + end end; main(_Args) -> ?ERROR("~s", [usage()]), erlang:halt(1). -inject(Dir) -> - RelupFiles = filelib:wildcard(filename:join([Dir, "releases", "*", "relup"])), - lists:foreach(fun(File) -> - case file:consult(File) of - {ok, [{CurrRelVsn, UpVsnRUs, DnVsnRUs}]} -> - ?INFO("injecting instructions to: ~p", [File]), - UpdatedContent = [{CurrRelVsn, inject_relup_instrs(CurrRelVsn, UpVsnRUs), - inject_relup_instrs(CurrRelVsn, DnVsnRUs)}], - file:write_file("/tmp/" ++ filename:basename(File), term_to_text(UpdatedContent)); - {ok, _BadFormat} -> - ?ERROR("bad formatted relup file: ~p", [File]); - {error, Reason} -> - ?ERROR("read relup file ~p failed: ~p", [File, Reason]) - end - end, RelupFiles). +inject_dir(Dir) -> + RelupFiles = filelib:wildcard(filename:join([Dir, "**", "relup"])), + lists:foreach(fun inject_file/1, RelupFiles). -inject_relup_instrs(CurrRelVsn, RUs) -> - [{Vsn, Desc, append_emqx_relup_instrs(CurrRelVsn, Vsn, Instrs)} || {Vsn, Desc, Instrs} <- RUs]. +inject_file(File) -> + case file:script(File) of + {ok, {CurrRelVsn, UpVsnRUs, DnVsnRUs}} -> + ?INFO("injecting instructions to: ~p", [File]), + UpdatedContent = {CurrRelVsn, inject_relup_instrs(up, CurrRelVsn, UpVsnRUs), + inject_relup_instrs(down, CurrRelVsn, DnVsnRUs)}, + ok = file:write_file(File, term_to_text(UpdatedContent)); + {ok, _BadFormat} -> + ?ERROR("bad formatted relup file: ~p", [File]); + {error, Reason} -> + ?ERROR("read relup file ~p failed: ~p", [File, Reason]) + end. + +inject_relup_instrs(Type, CurrRelVsn, RUs) -> + [{Vsn, Desc, append_emqx_relup_instrs(Type, CurrRelVsn, Vsn, Instrs)} || {Vsn, Desc, Instrs} <- RUs]. %% The `{apply, emqx_relup, post_release_upgrade, []}` will be appended to the end of %% the instruction lists. -append_emqx_relup_instrs(CurrRelVsn, Vsn, Instrs) -> +append_emqx_relup_instrs(Type, CurrRelVsn, Vsn, Instrs) -> + CallbackFun = relup_callback_func(Type), case lists:reverse(Instrs) of - [{apply, {emqx_relup, post_release_upgrade, _}} | _] -> + [{apply, {emqx_relup, CallbackFun, _}} | _] -> Instrs; RInstrs -> - lists:reverse([instr_post_release_upgrade(CurrRelVsn, Vsn) | RInstrs]) + lists:reverse([ {load_object_code, {emqx, CurrRelVsn, [emqx_relup]}} + , {load, {emqx_relup, brutal_purge, soft_purge}} + , {apply, {emqx_relup, CallbackFun, [CurrRelVsn, Vsn]}} + | RInstrs]) end. -instr_post_release_upgrade(CurrRelVsn, Vsn) -> - {apply, {emqx_relup, post_release_upgrade, [CurrRelVsn, Vsn]}}. +relup_callback_func(up) -> post_release_upgrade; +relup_callback_func(down) -> post_release_downgrade. term_to_text(Term) -> - io_lib:format("~p.", [Term]). \ No newline at end of file + io_lib:format("~p.", [Term]). From 17440b2b53da924343e8d58c64d29fa3ad70630d Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Sun, 27 Feb 2022 10:23:14 +0800 Subject: [PATCH 04/13] fix(relup): add module emqx_relup --- scripts/inject-relup.escript | 3 ++- src/emqx_relup.erl | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/emqx_relup.erl diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript index 86c541ada..e414b9c31 100755 --- a/scripts/inject-relup.escript +++ b/scripts/inject-relup.escript @@ -49,13 +49,14 @@ inject_relup_instrs(Type, CurrRelVsn, RUs) -> %% the instruction lists. append_emqx_relup_instrs(Type, CurrRelVsn, Vsn, Instrs) -> CallbackFun = relup_callback_func(Type), + Extra = #{}, %% we may need some extended args case lists:reverse(Instrs) of [{apply, {emqx_relup, CallbackFun, _}} | _] -> Instrs; RInstrs -> lists:reverse([ {load_object_code, {emqx, CurrRelVsn, [emqx_relup]}} , {load, {emqx_relup, brutal_purge, soft_purge}} - , {apply, {emqx_relup, CallbackFun, [CurrRelVsn, Vsn]}} + , {apply, {emqx_relup, CallbackFun, [CurrRelVsn, Vsn, Extra]}} | RInstrs]) end. diff --git a/src/emqx_relup.erl b/src/emqx_relup.erl new file mode 100644 index 000000000..e844ea0ba --- /dev/null +++ b/src/emqx_relup.erl @@ -0,0 +1,17 @@ +-module(emqx_relup). + +-export([ post_release_upgrade/3 + , post_release_downgrade/3 + ]). + +post_release_upgrade(_CurrRelVsn, _FromVsn, _) -> + reload_components(). + +post_release_downgrade(_CurrRelVsn, _ToVsn, _) -> + reload_components(). + +reload_components() -> + io:format("reloading resource providers ..."), + emqx_rule_engine:load_providers(), + io:format("loading plugins ..."), + emqx_plugins:load(). From 28bd2fcfa463dc04a01bdb07d11521e51f0294d1 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Sun, 27 Feb 2022 20:04:21 +0800 Subject: [PATCH 05/13] fix(relup): release upgrade failed with {bad_lib_vsn,emqx,"4.4.2"} --- scripts/inject-relup.escript | 46 +++++++++++++++++++++++++++--------- src/emqx_relup.erl | 14 +++++++++-- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript index e414b9c31..1a2dae78a 100755 --- a/scripts/inject-relup.escript +++ b/scripts/inject-relup.escript @@ -30,38 +30,62 @@ inject_dir(Dir) -> lists:foreach(fun inject_file/1, RelupFiles). inject_file(File) -> + EmqxVsn = emqx_vsn_from_rel_file(File), case file:script(File) of {ok, {CurrRelVsn, UpVsnRUs, DnVsnRUs}} -> ?INFO("injecting instructions to: ~p", [File]), - UpdatedContent = {CurrRelVsn, inject_relup_instrs(up, CurrRelVsn, UpVsnRUs), - inject_relup_instrs(down, CurrRelVsn, DnVsnRUs)}, + UpdatedContent = {CurrRelVsn, inject_relup_instrs(up, EmqxVsn, CurrRelVsn, UpVsnRUs), + inject_relup_instrs(down, EmqxVsn, CurrRelVsn, DnVsnRUs)}, ok = file:write_file(File, term_to_text(UpdatedContent)); {ok, _BadFormat} -> - ?ERROR("bad formatted relup file: ~p", [File]); + ?ERROR("bad formatted relup file: ~p", [File]), + error({bad_relup_format, File}); {error, Reason} -> - ?ERROR("read relup file ~p failed: ~p", [File, Reason]) + ?ERROR("read relup file ~p failed: ~p", [File, Reason]), + error({read_relup_error, Reason}) end. -inject_relup_instrs(Type, CurrRelVsn, RUs) -> - [{Vsn, Desc, append_emqx_relup_instrs(Type, CurrRelVsn, Vsn, Instrs)} || {Vsn, Desc, Instrs} <- RUs]. +inject_relup_instrs(Type, EmqxVsn, CurrRelVsn, RUs) -> + [{Vsn, Desc, append_emqx_relup_instrs(Type, EmqxVsn, CurrRelVsn, Vsn, Instrs)} + || {Vsn, Desc, Instrs} <- RUs]. %% The `{apply, emqx_relup, post_release_upgrade, []}` will be appended to the end of %% the instruction lists. -append_emqx_relup_instrs(Type, CurrRelVsn, Vsn, Instrs) -> +append_emqx_relup_instrs(Type, EmqxVsn, CurrRelVsn, Vsn, Instrs) -> CallbackFun = relup_callback_func(Type), Extra = #{}, %% we may need some extended args case lists:reverse(Instrs) of [{apply, {emqx_relup, CallbackFun, _}} | _] -> Instrs; RInstrs -> - lists:reverse([ {load_object_code, {emqx, CurrRelVsn, [emqx_relup]}} - , {load, {emqx_relup, brutal_purge, soft_purge}} - , {apply, {emqx_relup, CallbackFun, [CurrRelVsn, Vsn, Extra]}} - | RInstrs]) + Instrs2 = lists:reverse( + [ {apply, {emqx_relup, CallbackFun, [CurrRelVsn, Vsn, Extra]}} + , {load, {emqx_relup, brutal_purge, soft_purge}} + | RInstrs]), + %% we have to put 'load_object_code' before 'point_of_no_return' + %% so here we simply put it to the beginning + [{load_object_code, {emqx, EmqxVsn, [emqx_relup]}} | Instrs2] end. relup_callback_func(up) -> post_release_upgrade; relup_callback_func(down) -> post_release_downgrade. +emqx_vsn_from_rel_file(RelupFile) -> + RelDir = filename:dirname(RelupFile), + RelFile = filename:join([RelDir, "emqx.rel"]), + case file:script(RelFile) of + {ok, {release, {_RelName, _RelVsn}, _Erts, Apps}} -> + case lists:keysearch(emqx, 1, Apps) of + {value, {emqx, EmqxVsn}} -> + EmqxVsn; + false -> + error({emqx_vsn_cannot_found, RelFile}) + end; + {ok, _BadFormat} -> + ?ERROR("bad formatted .rel file: ~p", [RelFile]); + {error, Reason} -> + ?ERROR("read .rel file ~p failed: ~p", [RelFile, Reason]) + end. + term_to_text(Term) -> io_lib:format("~p.", [Term]). diff --git a/src/emqx_relup.erl b/src/emqx_relup.erl index e844ea0ba..b435f8ae8 100644 --- a/src/emqx_relup.erl +++ b/src/emqx_relup.erl @@ -10,8 +10,18 @@ post_release_upgrade(_CurrRelVsn, _FromVsn, _) -> post_release_downgrade(_CurrRelVsn, _ToVsn, _) -> reload_components(). +-ifdef(EMQX_ENTERPRISE). reload_components() -> - io:format("reloading resource providers ..."), + io:format("reloading resource providers ...~n"), emqx_rule_engine:load_providers(), - io:format("loading plugins ..."), + io:format("reloading module providers ...~n"), + emqx_modules:load_providers(), + io:format("loading plugins ...~n"), emqx_plugins:load(). +-else. +reload_components() -> + io:format("reloading resource providers ...~n"), + emqx_rule_engine:load_providers(), + io:format("loading plugins ...~n"), + emqx_plugins:load(). +-endif. From b7d07d7a96f30b29e9611cf795b69b6e54456563 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Mon, 28 Feb 2022 10:05:41 +0800 Subject: [PATCH 06/13] fix(appup): remove the post relup scripts from emqx_dashboard.appup.src The instructions have been move into module `emqx_relup`. --- lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src b/lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src index 902585ffb..270c65b5e 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src @@ -1,18 +1,11 @@ %% -*- mode: erlang -*- {VSN, [ {<<".*">>, - %% load all plugins - %% NOTE: this depends on the fact that emqx_dashboard is always - %% the last application gets upgraded - [ {apply, {emqx_rule_engine, load_providers, []}} - , {restart_application, emqx_dashboard} - , {apply, {emqx_plugins, load, []}} + [ {restart_application, emqx_dashboard} ]} ], [ {<<".*">>, - [ {apply, {emqx_rule_engine, load_providers, []}} - , {restart_application, emqx_dashboard} - , {apply, {emqx_plugins, load, []}} + [ {restart_application, emqx_dashboard} ]} ] -}. +}. \ No newline at end of file From f25b8801b4a83e7a15e2a6a574f3d85460bc7336 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Mon, 28 Feb 2022 19:07:38 +0800 Subject: [PATCH 07/13] fix(build): also make SYSTEM configurable by env variable --- build | 2 +- scripts/pkg-full-vsn.sh | 2 +- scripts/relup-base-packages.sh | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build b/build index a339f803b..b4c9d0548 100755 --- a/build +++ b/build @@ -18,7 +18,7 @@ cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" PKG_VSN="${PKG_VSN:-$(./pkg-vsn.sh)}" export PKG_VSN -SYSTEM="$(./scripts/get-distro.sh)" +SYSTEM="${SYSTEM:-$(./scripts/get-distro.sh)}" ## ## Support RPM and Debian based linux systems diff --git a/scripts/pkg-full-vsn.sh b/scripts/pkg-full-vsn.sh index de32e11ef..e118643c9 100755 --- a/scripts/pkg-full-vsn.sh +++ b/scripts/pkg-full-vsn.sh @@ -27,7 +27,7 @@ esac cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")/.." OTP_VSN="${OTP_VSN:-$(./scripts/get-otp-vsn.sh)}" -SYSTEM="$(./scripts/get-distro.sh)" +SYSTEM="${SYSTEM:-$(./scripts/get-distro.sh)}" UNAME="$(uname -m)" case "$UNAME" in diff --git a/scripts/relup-base-packages.sh b/scripts/relup-base-packages.sh index 2a47b5135..070f3926a 100755 --- a/scripts/relup-base-packages.sh +++ b/scripts/relup-base-packages.sh @@ -62,10 +62,11 @@ pushd _upgrade_base for tag in $(../scripts/relup-base-vsns.sh $EDITION | xargs echo -n); do filename="$PROFILE-${tag#[e|v]}-otp$OTP_VSN-$SYSTEM-$ARCH.zip" url="https://www.emqx.com/downloads/$DIR/${tag#[e|v]}/$filename" - echo "downloading base package from ${url} ..." if [ ! -f "$filename" ] && curl -I -m 10 -o /dev/null -s -w "%{http_code}" "${url}" | grep -q -oE "^[23]+" ; then + echo "downloading base package from ${url} ..." curl -L -o "${filename}" "${url}" if [ "$SYSTEM" != "centos6" ]; then + echo "downloading sha256 sum from ${url}.sha256 ..." curl -L -o "${filename}.sha256" "${url}.sha256" SUMSTR=$(cat "${filename}.sha256") echo "got sha265sum: ${SUMSTR}" From a7791b6c586f238a5b2f172aff01ec480bccb477 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Tue, 1 Mar 2022 01:58:55 +0800 Subject: [PATCH 08/13] fix(relup): download to 4.4.0 failed with error bad_lib_vsn --- rebar.config.erl | 2 +- scripts/inject-relup.escript | 175 +++++++++++++++++++++-------------- src/emqx_relup.erl | 10 +- 3 files changed, 112 insertions(+), 75 deletions(-) diff --git a/rebar.config.erl b/rebar.config.erl index 447e58f97..32e3444d1 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -129,7 +129,7 @@ prod_overrides() -> [{add, [ {erl_opts, [deterministic]}]}]. relup_deps(Profile, Vsn) -> - InjectCmd = "scripts/inject-relup.escript " ++ filename:join(["_build", Profile, "rel", "emqx", "releases", Vsn]), + InjectCmd = "scripts/inject-relup.escript " ++ filename:join(["_build", Profile, "rel", "emqx"]) ++ " " ++ Vsn, {post_hooks, [ {"(linux|darwin|solaris|freebsd|netbsd|openbsd)", relup, InjectCmd} ] diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript index 1a2dae78a..b860ab0d9 100755 --- a/scripts/inject-relup.escript +++ b/scripts/inject-relup.escript @@ -4,88 +4,123 @@ -mode(compile). --define(ERROR(FORMAT, ARGS), io:format(standard_error, "[inject-relup] " ++ FORMAT ++"~n", ARGS)). --define(INFO(FORMAT, ARGS), io:format(user, "[inject-relup] " ++ FORMAT ++"~n", ARGS)). +-define(ERROR(FORMAT, ARGS), io:format(standard_error, "[inject-relup] " ++ FORMAT ++ "~n", ARGS)). +-define(INFO(FORMAT, ARGS), io:format(user, "[inject-relup] " ++ FORMAT ++ "~n", ARGS)). usage() -> - "Usage: " ++ escript:script_name() ++ " ". + "Usage: " ++ escript:script_name() ++ " ". -main([DirOrFile]) -> - case filelib:is_dir(DirOrFile) of - true -> ok = inject_dir(DirOrFile); - false -> - case filelib:is_regular(DirOrFile) of - true -> inject_file(DirOrFile); +main([RelRootDir, CurrRelVsn]) -> + case filelib:is_dir(filename:join([RelRootDir, "releases"])) andalso + filelib:is_dir(filename:join([RelRootDir, "lib"])) of + true -> + EmqxAppVsns = get_emqx_app_vsns(RelRootDir), + ok = inject_relup_file(RelRootDir, CurrRelVsn, EmqxAppVsns); false -> - ?ERROR("not a valid file: ~p", [DirOrFile]), - erlang:halt(1) - end - end; + ?ERROR("not a valid root dir of release: ~p, for example: _build/emqx/rel/emqx", + [RelRootDir]), + erlang:halt(1) + end; main(_Args) -> - ?ERROR("~s", [usage()]), - erlang:halt(1). + ?ERROR("~s", [usage()]), + erlang:halt(1). -inject_dir(Dir) -> - RelupFiles = filelib:wildcard(filename:join([Dir, "**", "relup"])), - lists:foreach(fun inject_file/1, RelupFiles). +inject_relup_file(RelRootDir, CurrRelVsn, EmqxAppVsns) -> + RelupFile = filename:join([RelRootDir, "releases", CurrRelVsn, "relup"]), + inject_file(RelupFile, EmqxAppVsns). -inject_file(File) -> - EmqxVsn = emqx_vsn_from_rel_file(File), - case file:script(File) of - {ok, {CurrRelVsn, UpVsnRUs, DnVsnRUs}} -> - ?INFO("injecting instructions to: ~p", [File]), - UpdatedContent = {CurrRelVsn, inject_relup_instrs(up, EmqxVsn, CurrRelVsn, UpVsnRUs), - inject_relup_instrs(down, EmqxVsn, CurrRelVsn, DnVsnRUs)}, - ok = file:write_file(File, term_to_text(UpdatedContent)); - {ok, _BadFormat} -> - ?ERROR("bad formatted relup file: ~p", [File]), - error({bad_relup_format, File}); - {error, Reason} -> - ?ERROR("read relup file ~p failed: ~p", [File, Reason]), - error({read_relup_error, Reason}) - end. +inject_file(File, EmqxAppVsns) -> + case file:script(File) of + {ok, {CurrRelVsn, UpVsnRUs, DnVsnRUs}} -> + ?INFO("injecting instructions to: ~p", [File]), + UpdatedContent = {CurrRelVsn, + inject_relup_instrs(up, EmqxAppVsns, CurrRelVsn, UpVsnRUs), + inject_relup_instrs(down, EmqxAppVsns, CurrRelVsn, DnVsnRUs)}, + file:write_file(File, term_to_text(UpdatedContent)); + {ok, _BadFormat} -> + ?ERROR("bad formatted relup file: ~p", [File]), + error({bad_relup_format, File}); + {error, enoent} -> + ?INFO("relup file not found: ~p", [File]), + ok; + {error, Reason} -> + ?ERROR("read relup file ~p failed: ~p", [File, Reason]), + error({read_relup_error, Reason}) + end. -inject_relup_instrs(Type, EmqxVsn, CurrRelVsn, RUs) -> - [{Vsn, Desc, append_emqx_relup_instrs(Type, EmqxVsn, CurrRelVsn, Vsn, Instrs)} - || {Vsn, Desc, Instrs} <- RUs]. +inject_relup_instrs(Type, EmqxAppVsns, CurrRelVsn, RUs) -> + lists:map(fun + ({Vsn, "(relup-injected) " ++ _ = Desc, Instrs}) -> %% already injected + {Vsn, Desc, Instrs}; + ({Vsn, Desc, Instrs}) -> + {Vsn, "(relup-injected) " ++ Desc, + append_emqx_relup_instrs(Type, EmqxAppVsns, CurrRelVsn, Vsn, Instrs)} + end, RUs). %% The `{apply, emqx_relup, post_release_upgrade, []}` will be appended to the end of %% the instruction lists. -append_emqx_relup_instrs(Type, EmqxVsn, CurrRelVsn, Vsn, Instrs) -> - CallbackFun = relup_callback_func(Type), - Extra = #{}, %% we may need some extended args - case lists:reverse(Instrs) of - [{apply, {emqx_relup, CallbackFun, _}} | _] -> - Instrs; - RInstrs -> - Instrs2 = lists:reverse( - [ {apply, {emqx_relup, CallbackFun, [CurrRelVsn, Vsn, Extra]}} - , {load, {emqx_relup, brutal_purge, soft_purge}} - | RInstrs]), - %% we have to put 'load_object_code' before 'point_of_no_return' - %% so here we simply put it to the beginning - [{load_object_code, {emqx, EmqxVsn, [emqx_relup]}} | Instrs2] - end. +append_emqx_relup_instrs(up, EmqxAppVsns, CurrRelVsn, FromRelVsn, Instrs) -> + {EmqxVsn, true} = maps:get(CurrRelVsn, EmqxAppVsns), + Extra = #{}, %% we may need some extended args + %% we have to put 'load_object_code' before 'point_of_no_return' + %% so here we simply put it to the beginning + Instrs0 = [ {load_object_code, {emqx, EmqxVsn, [emqx_relup]}} + | Instrs], + Instrs0 ++ + [ {load, {emqx_relup, brutal_purge, soft_purge}} + , {apply, {emqx_relup, post_release_upgrade, [FromRelVsn, Extra]}} + ]; -relup_callback_func(up) -> post_release_upgrade; -relup_callback_func(down) -> post_release_downgrade. +append_emqx_relup_instrs(down, EmqxAppVsns, _CurrRelVsn, ToRelVsn, Instrs) -> + Extra = #{}, %% we may need some extended args + case maps:get(ToRelVsn, EmqxAppVsns) of + {EmqxVsn, true} -> + Instrs0 = [ {load_object_code, {emqx, EmqxVsn, [emqx_relup]}} + | Instrs], + Instrs0 ++ + [ {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, Extra]}} + , {load, {emqx_relup, brutal_purge, soft_purge}} + ]; + {_EmqxVsn, false} -> + Instrs ++ + [ {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, Extra]}} + , {remove, {emqx_relup, brutal_purge, soft_purge}} + ] + end. -emqx_vsn_from_rel_file(RelupFile) -> - RelDir = filename:dirname(RelupFile), - RelFile = filename:join([RelDir, "emqx.rel"]), - case file:script(RelFile) of - {ok, {release, {_RelName, _RelVsn}, _Erts, Apps}} -> - case lists:keysearch(emqx, 1, Apps) of - {value, {emqx, EmqxVsn}} -> - EmqxVsn; - false -> - error({emqx_vsn_cannot_found, RelFile}) - end; - {ok, _BadFormat} -> - ?ERROR("bad formatted .rel file: ~p", [RelFile]); - {error, Reason} -> - ?ERROR("read .rel file ~p failed: ~p", [RelFile, Reason]) - end. +get_emqx_app_vsns(RelRootDir) -> + RelFiles = filelib:wildcard(filename:join([RelRootDir, "releases", "*", "emqx.rel"])), + lists:foldl(fun(RelFile, AppVsns) -> + {ok, RelVsn, EmqxVsn} = read_emqx_vsn_from_rel_file(RelFile), + AppVsns#{RelVsn => {EmqxVsn, has_relup_module(RelRootDir, EmqxVsn)}} + end, #{}, RelFiles). + +read_emqx_vsn_from_rel_file(RelFile) -> + case file:script(RelFile) of + {ok, {release, {_RelName, RelVsn}, _Erts, Apps}} -> + case lists:keysearch(emqx, 1, Apps) of + {value, {emqx, EmqxVsn}} -> + {ok, RelVsn, EmqxVsn}; + false -> + error({emqx_vsn_cannot_found, RelFile}) + end; + {ok, _BadFormat} -> + ?ERROR("bad formatted .rel file: ~p", [RelFile]); + {error, Reason} -> + ?ERROR("read .rel file ~p failed: ~p", [RelFile, Reason]) + end. + +has_relup_module(RelRootDir, EmqxVsn) -> + AppFile = filename:join([RelRootDir, "lib", "emqx-" ++ EmqxVsn, "ebin", "emqx.app"]), + case file:script(AppFile) of + {ok, {application, emqx, AppInfo}} -> + {value, {_, EmqxVsn}} = lists:keysearch(vsn, 1, AppInfo), %% assert + {value, {_, Modules}} = lists:keysearch(modules, 1, AppInfo), + lists:member(emqx_relup, Modules); + {error, Reason} -> + ?ERROR("read .app file ~p failed: ~p", [AppFile, Reason]), + error({read_app_file_error, AppFile, Reason}) + end. term_to_text(Term) -> - io_lib:format("~p.", [Term]). + io_lib:format("~p.", [Term]). diff --git a/src/emqx_relup.erl b/src/emqx_relup.erl index b435f8ae8..66a5281da 100644 --- a/src/emqx_relup.erl +++ b/src/emqx_relup.erl @@ -1,13 +1,15 @@ -module(emqx_relup). --export([ post_release_upgrade/3 - , post_release_downgrade/3 +-export([ post_release_upgrade/2 + , post_release_downgrade/2 ]). -post_release_upgrade(_CurrRelVsn, _FromVsn, _) -> +%% what to do after upgraded from a old release vsn. +post_release_upgrade(_FromRelVsn, _) -> reload_components(). -post_release_downgrade(_CurrRelVsn, _ToVsn, _) -> +%% what to do after downgraded to a old release vsn. +post_release_downgrade(_ToRelVsn, _) -> reload_components(). -ifdef(EMQX_ENTERPRISE). From 769e79e2cd99f3e5a89f4a59574bfa6c49e6acd7 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Tue, 1 Mar 2022 15:09:04 +0800 Subject: [PATCH 09/13] chore(relup): also inject emqx_app automatically --- scripts/inject-relup.escript | 56 +++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript index b860ab0d9..3a779d239 100755 --- a/scripts/inject-relup.escript +++ b/scripts/inject-relup.escript @@ -59,32 +59,54 @@ inject_relup_instrs(Type, EmqxAppVsns, CurrRelVsn, RUs) -> %% The `{apply, emqx_relup, post_release_upgrade, []}` will be appended to the end of %% the instruction lists. -append_emqx_relup_instrs(up, EmqxAppVsns, CurrRelVsn, FromRelVsn, Instrs) -> +append_emqx_relup_instrs(up, EmqxAppVsns, CurrRelVsn, FromRelVsn, Instrs0) -> {EmqxVsn, true} = maps:get(CurrRelVsn, EmqxAppVsns), Extra = #{}, %% we may need some extended args + LoadObjEmqxMods = {load_object_code, {emqx, EmqxVsn, [emqx_relup, emqx_app]}}, + LoadCodeEmqxRelup = {load, {emqx_relup, brutal_purge, soft_purge}}, + LoadCodeEmqxApp = {load, {emqx_app, brutal_purge, soft_purge}}, + ApplyEmqxRelup = {apply, {emqx_relup, post_release_upgrade, [FromRelVsn, Extra]}}, + Instrs1 = Instrs0 -- [LoadCodeEmqxRelup, LoadCodeEmqxApp], %% we have to put 'load_object_code' before 'point_of_no_return' - %% so here we simply put it to the beginning - Instrs0 = [ {load_object_code, {emqx, EmqxVsn, [emqx_relup]}} - | Instrs], - Instrs0 ++ - [ {load, {emqx_relup, brutal_purge, soft_purge}} - , {apply, {emqx_relup, post_release_upgrade, [FromRelVsn, Extra]}} + %% so here we simply put them to the beginning of the instruction list + Instrs2 = [ LoadObjEmqxMods + | Instrs1], + %% the `load` must be put after the 'point_of_no_return' + Instrs2 ++ + [ LoadCodeEmqxRelup + , LoadCodeEmqxApp + , ApplyEmqxRelup ]; -append_emqx_relup_instrs(down, EmqxAppVsns, _CurrRelVsn, ToRelVsn, Instrs) -> +append_emqx_relup_instrs(down, EmqxAppVsns, _CurrRelVsn, ToRelVsn, Instrs0) -> Extra = #{}, %% we may need some extended args + ApplyEmqxRelup = {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, Extra]}}, case maps:get(ToRelVsn, EmqxAppVsns) of {EmqxVsn, true} -> - Instrs0 = [ {load_object_code, {emqx, EmqxVsn, [emqx_relup]}} - | Instrs], - Instrs0 ++ - [ {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, Extra]}} - , {load, {emqx_relup, brutal_purge, soft_purge}} + LoadObjEmqxMods = {load_object_code, {emqx, EmqxVsn, [emqx_relup, emqx_app]}}, + LoadCodeEmqxRelup = {load, {emqx_relup, brutal_purge, soft_purge}}, + LoadCodeEmqxApp = {load, {emqx_app, brutal_purge, soft_purge}}, + Instrs1 = Instrs0 -- [LoadCodeEmqxRelup, LoadCodeEmqxApp, ApplyEmqxRelup], + Instrs2 = [ LoadObjEmqxMods + | Instrs1], + %% NOTE: We apply emqx_relup:post_release_downgrade/2 first, and then reload + %% the old vsn code of emqx_relup. + Instrs2 ++ + [ LoadCodeEmqxApp + , ApplyEmqxRelup + , LoadCodeEmqxRelup ]; - {_EmqxVsn, false} -> - Instrs ++ - [ {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, Extra]}} - , {remove, {emqx_relup, brutal_purge, soft_purge}} + {EmqxVsn, false} -> + LoadObjEmqxApp = {load_object_code, {emqx, EmqxVsn, [emqx_app]}}, + LoadCodeEmqxApp = {load, {emqx_app, brutal_purge, soft_purge}}, + RemoveCodeEmqxRelup = {remove, {emqx_relup, brutal_purge, soft_purge}}, + Instrs1 = Instrs0 -- [LoadCodeEmqxApp, RemoveCodeEmqxRelup, ApplyEmqxRelup], + Instrs2 = [ LoadObjEmqxApp + | Instrs1], + Instrs2 ++ + [ LoadCodeEmqxApp + , ApplyEmqxRelup + , RemoveCodeEmqxRelup ] end. From 7698ad7c4b32701e8e3b3a25435d8d7d9bb135a1 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Tue, 1 Mar 2022 17:15:19 +0800 Subject: [PATCH 10/13] chore(relup): print more info after release upgrade complete --- src/emqx_relup.erl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/emqx_relup.erl b/src/emqx_relup.erl index 66a5281da..cb71c6305 100644 --- a/src/emqx_relup.erl +++ b/src/emqx_relup.erl @@ -4,26 +4,31 @@ , post_release_downgrade/2 ]). +-define(INFO(FORMAT), io:format("[emqx_relup] " ++ FORMAT ++ "~n")). +-define(INFO(FORMAT, ARGS), io:format("[emqx_relup] " ++ FORMAT ++ "~n", ARGS)). + %% what to do after upgraded from a old release vsn. post_release_upgrade(_FromRelVsn, _) -> + ?INFO("emqx has been upgraded to ~s", [emqx_app:get_release()]), reload_components(). %% what to do after downgraded to a old release vsn. post_release_downgrade(_ToRelVsn, _) -> + ?INFO("emqx has been downgrade to ~s", [emqx_app:get_release()]), reload_components(). -ifdef(EMQX_ENTERPRISE). reload_components() -> - io:format("reloading resource providers ...~n"), + ?INFO("reloading resource providers ..."), emqx_rule_engine:load_providers(), - io:format("reloading module providers ...~n"), + ?INFO("reloading module providers ..."), emqx_modules:load_providers(), - io:format("loading plugins ...~n"), + ?INFO("loading plugins ..."), emqx_plugins:load(). -else. reload_components() -> - io:format("reloading resource providers ...~n"), + ?INFO("reloading resource providers ..."), emqx_rule_engine:load_providers(), - io:format("loading plugins ...~n"), + ?INFO("loading plugins ..."), emqx_plugins:load(). -endif. From 41afbd2b13ddcbd77c911b9193c4cfa7a7e2f1d4 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Tue, 1 Mar 2022 22:01:00 +0800 Subject: [PATCH 11/13] refactor(relup): don't collect relvsn -> appvsn mappings --- build | 3 + .../src/emqx_dashboard.appup.src | 2 +- rebar.config.erl | 11 -- scripts/inject-relup.escript | 165 +++++++----------- src/emqx.appup.src | 14 +- src/emqx_relup.erl | 30 +++- 6 files changed, 107 insertions(+), 118 deletions(-) diff --git a/build b/build index b4c9d0548..8bf435357 100755 --- a/build +++ b/build @@ -136,6 +136,9 @@ make_zip() { local tarball="${relpath}/${tarname}" local target_zip="${pkgpath}/${pkgname}" tar zxf "${tarball}" -C "${tard}/emqx" + if ! [[ $SYSTEM == windows* ]]; then + ./scripts/inject-relup.escript "${tard}/emqx/releases/${PKG_VSN}/relup" + fi cp_dyn_libs "${tard}/emqx" pushd "${tard}" >/dev/null case "$SYSTEM" in diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src b/lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src index 270c65b5e..513757418 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard.appup.src @@ -8,4 +8,4 @@ [ {restart_application, emqx_dashboard} ]} ] -}. \ No newline at end of file +}. diff --git a/rebar.config.erl b/rebar.config.erl index 32e3444d1..492707867 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -128,34 +128,23 @@ prod_compile_opts() -> prod_overrides() -> [{add, [ {erl_opts, [deterministic]}]}]. -relup_deps(Profile, Vsn) -> - InjectCmd = "scripts/inject-relup.escript " ++ filename:join(["_build", Profile, "rel", "emqx"]) ++ " " ++ Vsn, - {post_hooks, - [ {"(linux|darwin|solaris|freebsd|netbsd|openbsd)", relup, InjectCmd} - ] - }. - profiles() -> Vsn = get_vsn(), [ {'emqx', [ {erl_opts, prod_compile_opts()} , {relx, relx(Vsn, cloud, bin)} , {overrides, prod_overrides()} - , relup_deps('emqx', Vsn) ]} , {'emqx-pkg', [ {erl_opts, prod_compile_opts()} , {relx, relx(Vsn, cloud, pkg)} , {overrides, prod_overrides()} - , relup_deps('emqx-pkg', Vsn) ]} , {'emqx-edge', [ {erl_opts, prod_compile_opts()} , {relx, relx(Vsn, edge, bin)} , {overrides, prod_overrides()} - , relup_deps('emqx-edge', Vsn) ]} , {'emqx-edge-pkg', [ {erl_opts, prod_compile_opts()} , {relx, relx(Vsn, edge, pkg)} , {overrides, prod_overrides()} - , relup_deps('emqx-edge-pkg', Vsn) ]} , {check, [ {erl_opts, common_compile_opts()} ]} diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript index 3a779d239..93fe34a90 100755 --- a/scripts/inject-relup.escript +++ b/scripts/inject-relup.escript @@ -8,34 +8,27 @@ -define(INFO(FORMAT, ARGS), io:format(user, "[inject-relup] " ++ FORMAT ++ "~n", ARGS)). usage() -> - "Usage: " ++ escript:script_name() ++ " ". + "Usage: " ++ escript:script_name() ++ " ". -main([RelRootDir, CurrRelVsn]) -> - case filelib:is_dir(filename:join([RelRootDir, "releases"])) andalso - filelib:is_dir(filename:join([RelRootDir, "lib"])) of +main([RelupFile]) -> + case filelib:is_regular(RelupFile) of true -> - EmqxAppVsns = get_emqx_app_vsns(RelRootDir), - ok = inject_relup_file(RelRootDir, CurrRelVsn, EmqxAppVsns); + ok = inject_relup_file(RelupFile); false -> - ?ERROR("not a valid root dir of release: ~p, for example: _build/emqx/rel/emqx", - [RelRootDir]), + ?ERROR("not a valid file: ~p", [RelupFile]), erlang:halt(1) end; main(_Args) -> ?ERROR("~s", [usage()]), erlang:halt(1). -inject_relup_file(RelRootDir, CurrRelVsn, EmqxAppVsns) -> - RelupFile = filename:join([RelRootDir, "releases", CurrRelVsn, "relup"]), - inject_file(RelupFile, EmqxAppVsns). - -inject_file(File, EmqxAppVsns) -> +inject_relup_file(File) -> case file:script(File) of {ok, {CurrRelVsn, UpVsnRUs, DnVsnRUs}} -> ?INFO("injecting instructions to: ~p", [File]), UpdatedContent = {CurrRelVsn, - inject_relup_instrs(up, EmqxAppVsns, CurrRelVsn, UpVsnRUs), - inject_relup_instrs(down, EmqxAppVsns, CurrRelVsn, DnVsnRUs)}, + inject_relup_instrs(up, UpVsnRUs), + inject_relup_instrs(down, DnVsnRUs)}, file:write_file(File, term_to_text(UpdatedContent)); {ok, _BadFormat} -> ?ERROR("bad formatted relup file: ~p", [File]), @@ -48,101 +41,77 @@ inject_file(File, EmqxAppVsns) -> error({read_relup_error, Reason}) end. -inject_relup_instrs(Type, EmqxAppVsns, CurrRelVsn, RUs) -> - lists:map(fun - ({Vsn, "(relup-injected) " ++ _ = Desc, Instrs}) -> %% already injected - {Vsn, Desc, Instrs}; - ({Vsn, Desc, Instrs}) -> - {Vsn, "(relup-injected) " ++ Desc, - append_emqx_relup_instrs(Type, EmqxAppVsns, CurrRelVsn, Vsn, Instrs)} +inject_relup_instrs(Type, RUs) -> + lists:map(fun({Vsn, Desc, Instrs}) -> + {Vsn, Desc, append_emqx_relup_instrs(Type, Vsn, Instrs)} end, RUs). %% The `{apply, emqx_relup, post_release_upgrade, []}` will be appended to the end of %% the instruction lists. -append_emqx_relup_instrs(up, EmqxAppVsns, CurrRelVsn, FromRelVsn, Instrs0) -> - {EmqxVsn, true} = maps:get(CurrRelVsn, EmqxAppVsns), +append_emqx_relup_instrs(up, FromRelVsn, Instrs0) -> Extra = #{}, %% we may need some extended args - LoadObjEmqxMods = {load_object_code, {emqx, EmqxVsn, [emqx_relup, emqx_app]}}, - LoadCodeEmqxRelup = {load, {emqx_relup, brutal_purge, soft_purge}}, - LoadCodeEmqxApp = {load, {emqx_app, brutal_purge, soft_purge}}, - ApplyEmqxRelup = {apply, {emqx_relup, post_release_upgrade, [FromRelVsn, Extra]}}, - Instrs1 = Instrs0 -- [LoadCodeEmqxRelup, LoadCodeEmqxApp], - %% we have to put 'load_object_code' before 'point_of_no_return' - %% so here we simply put them to the beginning of the instruction list - Instrs2 = [ LoadObjEmqxMods - | Instrs1], - %% the `load` must be put after the 'point_of_no_return' - Instrs2 ++ - [ LoadCodeEmqxRelup - , LoadCodeEmqxApp - , ApplyEmqxRelup + filter_and_check_instrs(up, Instrs0) ++ + [ {load, {emqx_app, brutal_purge, soft_purge}} + , {load, {emqx_relup, brutal_purge, soft_purge}} + , {apply, {emqx_relup, post_release_upgrade, [FromRelVsn, Extra]}} ]; -append_emqx_relup_instrs(down, EmqxAppVsns, _CurrRelVsn, ToRelVsn, Instrs0) -> +append_emqx_relup_instrs(down, ToRelVsn, Instrs0) -> Extra = #{}, %% we may need some extended args - ApplyEmqxRelup = {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, Extra]}}, - case maps:get(ToRelVsn, EmqxAppVsns) of - {EmqxVsn, true} -> - LoadObjEmqxMods = {load_object_code, {emqx, EmqxVsn, [emqx_relup, emqx_app]}}, - LoadCodeEmqxRelup = {load, {emqx_relup, brutal_purge, soft_purge}}, - LoadCodeEmqxApp = {load, {emqx_app, brutal_purge, soft_purge}}, - Instrs1 = Instrs0 -- [LoadCodeEmqxRelup, LoadCodeEmqxApp, ApplyEmqxRelup], - Instrs2 = [ LoadObjEmqxMods - | Instrs1], - %% NOTE: We apply emqx_relup:post_release_downgrade/2 first, and then reload - %% the old vsn code of emqx_relup. - Instrs2 ++ - [ LoadCodeEmqxApp - , ApplyEmqxRelup - , LoadCodeEmqxRelup - ]; - {EmqxVsn, false} -> - LoadObjEmqxApp = {load_object_code, {emqx, EmqxVsn, [emqx_app]}}, - LoadCodeEmqxApp = {load, {emqx_app, brutal_purge, soft_purge}}, - RemoveCodeEmqxRelup = {remove, {emqx_relup, brutal_purge, soft_purge}}, - Instrs1 = Instrs0 -- [LoadCodeEmqxApp, RemoveCodeEmqxRelup, ApplyEmqxRelup], - Instrs2 = [ LoadObjEmqxApp - | Instrs1], - Instrs2 ++ - [ LoadCodeEmqxApp - , ApplyEmqxRelup - , RemoveCodeEmqxRelup - ] + %% NOTE: When downgrading, we apply emqx_relup:post_release_downgrade/2 before reloading + %% or removing the emqx_relup module. + Instrs1 = filter_and_check_instrs(down, Instrs0) ++ + [ {load, {emqx_app, brutal_purge, soft_purge}} + , {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, Extra]}} + ], + %% emqx_relup does not exist before release "4.4.2" + LoadInsts = + case ToRelVsn of + ToRelVsn when ToRelVsn =:= "4.4.1"; ToRelVsn =:= "4.4.0" -> + [{remove, {emqx_relup, brutal_purge, brutal_purge}}]; + _ -> + [{load, {emqx_relup, brutal_purge, soft_purge}}] + end, + Instrs1 ++ LoadInsts. + +filter_and_check_instrs(Type, Instrs) -> + case take_emqx_vsn_and_modules(Instrs) of + {EmqxAppVsn, EmqxMods, RemainInstrs} when EmqxAppVsn =/= not_found, EmqxMods =/= [] -> + assert_mandatory_modules(Type, EmqxMods), + [{load_object_code, {emqx, EmqxAppVsn, EmqxMods}} | RemainInstrs]; + {_, _, _} -> + ?ERROR("cannot found 'load_module' instructions for app emqx", []), + error({instruction_not_found, load_object_code}) end. -get_emqx_app_vsns(RelRootDir) -> - RelFiles = filelib:wildcard(filename:join([RelRootDir, "releases", "*", "emqx.rel"])), - lists:foldl(fun(RelFile, AppVsns) -> - {ok, RelVsn, EmqxVsn} = read_emqx_vsn_from_rel_file(RelFile), - AppVsns#{RelVsn => {EmqxVsn, has_relup_module(RelRootDir, EmqxVsn)}} - end, #{}, RelFiles). +take_emqx_vsn_and_modules(Instrs) -> + lists:foldl(fun + ({load_object_code, {emqx, AppVsn, Mods}}, {_EmqxAppVsn, EmqxMods, RemainInstrs}) -> + {AppVsn, EmqxMods ++ Mods, RemainInstrs}; + ({load, {Mod, _, _}}, {EmqxAppVsn, EmqxMods, RemainInstrs}) + when Mod =:= emqx_relup; Mod =:= emqx_app -> + {EmqxAppVsn, EmqxMods, RemainInstrs}; + ({remove, {emqx_relup, _, _}}, {EmqxAppVsn, EmqxMods, RemainInstrs}) -> + {EmqxAppVsn, EmqxMods, RemainInstrs}; + ({apply, {emqx_relup, _, _}}, {EmqxAppVsn, EmqxMods, RemainInstrs}) -> + {EmqxAppVsn, EmqxMods, RemainInstrs}; + (Instr, {EmqxAppVsn, EmqxMods, RemainInstrs}) -> + {EmqxAppVsn, EmqxMods, RemainInstrs ++ [Instr]} + end, {not_found, [], []}, Instrs). -read_emqx_vsn_from_rel_file(RelFile) -> - case file:script(RelFile) of - {ok, {release, {_RelName, RelVsn}, _Erts, Apps}} -> - case lists:keysearch(emqx, 1, Apps) of - {value, {emqx, EmqxVsn}} -> - {ok, RelVsn, EmqxVsn}; - false -> - error({emqx_vsn_cannot_found, RelFile}) - end; - {ok, _BadFormat} -> - ?ERROR("bad formatted .rel file: ~p", [RelFile]); - {error, Reason} -> - ?ERROR("read .rel file ~p failed: ~p", [RelFile, Reason]) - end. +assert_mandatory_modules(up, Mods) -> + assert(lists:member(emqx_relup, Mods) andalso lists:member(emqx_app, Mods), + "cannot found 'load_module' instructions for emqx_app and emqx_rel: ~p", [Mods]); -has_relup_module(RelRootDir, EmqxVsn) -> - AppFile = filename:join([RelRootDir, "lib", "emqx-" ++ EmqxVsn, "ebin", "emqx.app"]), - case file:script(AppFile) of - {ok, {application, emqx, AppInfo}} -> - {value, {_, EmqxVsn}} = lists:keysearch(vsn, 1, AppInfo), %% assert - {value, {_, Modules}} = lists:keysearch(modules, 1, AppInfo), - lists:member(emqx_relup, Modules); - {error, Reason} -> - ?ERROR("read .app file ~p failed: ~p", [AppFile, Reason]), - error({read_app_file_error, AppFile, Reason}) - end. +assert_mandatory_modules(down, Mods) -> + assert(lists:member(emqx_app, Mods), + "cannot found 'load_module' instructions for emqx_app", []). + +assert(true, _, _) -> + ok; +assert(false, Msg, Args) -> + ?ERROR(Msg, Args), + error(assert_failed). term_to_text(Term) -> io_lib:format("~p.", [Term]). diff --git a/src/emqx.appup.src b/src/emqx.appup.src index b9d4f5a16..53b4a9ff7 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -1,7 +1,9 @@ %% -*- mode: erlang -*- {VSN, [{"4.4.1", - [{load_module,emqx_os_mon,brutal_purge,soft_purge,[]}, + [{load_module,emqx_app,brutal_purge,soft_purge,[]}, + {add_module,emqx_relup}, + {load_module,emqx_os_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_sys_mon,brutal_purge,soft_purge,[]}, @@ -10,7 +12,8 @@ {load_module,emqx_ctl,brutal_purge,soft_purge,[]}, {load_module,emqx_channel,brutal_purge,soft_purge,[]}]}, {"4.4.0", - [{load_module,emqx_pmon,brutal_purge,soft_purge,[]}, + [{add_module,emqx_relup}, + {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_sys_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_vm_mon,brutal_purge,soft_purge,[]}, @@ -31,7 +34,9 @@ {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {<<".*">>,[]}], [{"4.4.1", - [{load_module,emqx_os_mon,brutal_purge,soft_purge,[]}, + [{load_module,emqx_app,brutal_purge,soft_purge,[]}, + {delete_module,emqx_relup}, + {load_module,emqx_os_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_sys_mon,brutal_purge,soft_purge,[]}, @@ -40,7 +45,8 @@ {load_module,emqx_ctl,brutal_purge,soft_purge,[]}, {load_module,emqx_channel,brutal_purge,soft_purge,[]}]}, {"4.4.0", - [{load_module,emqx_pmon,brutal_purge,soft_purge,[]}, + [{delete_module,emqx_relup}, + {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_sys_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_vm_mon,brutal_purge,soft_purge,[]}, diff --git a/src/emqx_relup.erl b/src/emqx_relup.erl index cb71c6305..dc443947a 100644 --- a/src/emqx_relup.erl +++ b/src/emqx_relup.erl @@ -1,5 +1,25 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2017-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + -module(emqx_relup). +%% NOTE: DO NOT remove this `-include`. +%% We use this to forece this module to upgraded every release. +-include("emqx_release.hrl"). + -export([ post_release_upgrade/2 , post_release_downgrade/2 ]). @@ -8,13 +28,15 @@ -define(INFO(FORMAT, ARGS), io:format("[emqx_relup] " ++ FORMAT ++ "~n", ARGS)). %% what to do after upgraded from a old release vsn. -post_release_upgrade(_FromRelVsn, _) -> - ?INFO("emqx has been upgraded to ~s", [emqx_app:get_release()]), +post_release_upgrade(FromRelVsn, _) -> + {_, CurrRelVsn} = ?EMQX_RELEASE, + ?INFO("emqx has been upgraded to from ~s to ~s!", [FromRelVsn, CurrRelVsn]), reload_components(). %% what to do after downgraded to a old release vsn. -post_release_downgrade(_ToRelVsn, _) -> - ?INFO("emqx has been downgrade to ~s", [emqx_app:get_release()]), +post_release_downgrade(ToRelVsn, _) -> + {_, CurrRelVsn} = ?EMQX_RELEASE, + ?INFO("emqx has been downgraded to from ~s to ~s!", [CurrRelVsn, ToRelVsn]), reload_components(). -ifdef(EMQX_ENTERPRISE). From 77e3c1d3ac35888097b958201f1e8a2bee066b55 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Wed, 2 Mar 2022 10:48:10 +0800 Subject: [PATCH 12/13] feat(relup): support providing 'Extra' parameter from appup.src --- scripts/inject-relup.escript | 68 +++++++++++++++++++++--------------- src/emqx_relup.erl | 6 ++-- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript index 93fe34a90..32c944246 100755 --- a/scripts/inject-relup.escript +++ b/scripts/inject-relup.escript @@ -49,20 +49,20 @@ inject_relup_instrs(Type, RUs) -> %% The `{apply, emqx_relup, post_release_upgrade, []}` will be appended to the end of %% the instruction lists. append_emqx_relup_instrs(up, FromRelVsn, Instrs0) -> - Extra = #{}, %% we may need some extended args - filter_and_check_instrs(up, Instrs0) ++ + {{UpExtra, _}, Instrs1} = filter_and_check_instrs(up, Instrs0), + Instrs1 ++ [ {load, {emqx_app, brutal_purge, soft_purge}} , {load, {emqx_relup, brutal_purge, soft_purge}} - , {apply, {emqx_relup, post_release_upgrade, [FromRelVsn, Extra]}} + , {apply, {emqx_relup, post_release_upgrade, [FromRelVsn, UpExtra]}} ]; append_emqx_relup_instrs(down, ToRelVsn, Instrs0) -> - Extra = #{}, %% we may need some extended args + {{_, DnExtra}, Instrs1} = filter_and_check_instrs(down, Instrs0) %% NOTE: When downgrading, we apply emqx_relup:post_release_downgrade/2 before reloading %% or removing the emqx_relup module. - Instrs1 = filter_and_check_instrs(down, Instrs0) ++ + Instrs1 ++ [ {load, {emqx_app, brutal_purge, soft_purge}} - , {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, Extra]}} + , {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, DnExtra]}} ], %% emqx_relup does not exist before release "4.4.2" LoadInsts = @@ -75,37 +75,49 @@ append_emqx_relup_instrs(down, ToRelVsn, Instrs0) -> Instrs1 ++ LoadInsts. filter_and_check_instrs(Type, Instrs) -> - case take_emqx_vsn_and_modules(Instrs) of - {EmqxAppVsn, EmqxMods, RemainInstrs} when EmqxAppVsn =/= not_found, EmqxMods =/= [] -> + case filter_fetch_emqx_mods_and_extra(Instrs) of + {_, _, [], _} -> + ?ERROR("cannot find any 'load_object_code' instructions for app emqx", []), + error({instruction_not_found, load_object_code}); + {UpExtra, DnExtra, EmqxMods, RemainInstrs} -> assert_mandatory_modules(Type, EmqxMods), - [{load_object_code, {emqx, EmqxAppVsn, EmqxMods}} | RemainInstrs]; - {_, _, _} -> - ?ERROR("cannot found 'load_module' instructions for app emqx", []), - error({instruction_not_found, load_object_code}) + {{UpExtra, DnExtra}, RemainInstrs} end. -take_emqx_vsn_and_modules(Instrs) -> - lists:foldl(fun - ({load_object_code, {emqx, AppVsn, Mods}}, {_EmqxAppVsn, EmqxMods, RemainInstrs}) -> - {AppVsn, EmqxMods ++ Mods, RemainInstrs}; - ({load, {Mod, _, _}}, {EmqxAppVsn, EmqxMods, RemainInstrs}) - when Mod =:= emqx_relup; Mod =:= emqx_app -> - {EmqxAppVsn, EmqxMods, RemainInstrs}; - ({remove, {emqx_relup, _, _}}, {EmqxAppVsn, EmqxMods, RemainInstrs}) -> - {EmqxAppVsn, EmqxMods, RemainInstrs}; - ({apply, {emqx_relup, _, _}}, {EmqxAppVsn, EmqxMods, RemainInstrs}) -> - {EmqxAppVsn, EmqxMods, RemainInstrs}; - (Instr, {EmqxAppVsn, EmqxMods, RemainInstrs}) -> - {EmqxAppVsn, EmqxMods, RemainInstrs ++ [Instr]} - end, {not_found, [], []}, Instrs). +filter_fetch_emqx_mods_and_extra(Instrs) -> + lists:foldl(fun do_filter_and_get/2, {UpExtra, DnExtra, [], []}, Instrs). + +%% collect modules for emqx app +do_filter_and_get({load_object_code, {emqx, _AppVsn, Mods}} = Instr, + {UpExtra, DnExtra, EmqxMods, RemainInstrs}) -> + {UpExtra, DnExtra, EmqxMods ++ Mods, RemainInstrs ++ [Instr]}; +%% remove 'load' instrs +do_filter_and_get({load, {Mod, _, _}}, {UpExtra, DnExtra, EmqxMods, RemainInstrs}) + when Mod =:= emqx_relup; Mod =:= emqx_app -> + {UpExtra, DnExtra, EmqxMods, RemainInstrs}; +%% remove 'remove' instrs +do_filter_and_get({remove, {emqx_relup, _, _}}, {UpExtra, DnExtra, EmqxMods, RemainInstrs}) -> + {UpExtra, DnExtra, EmqxMods, RemainInstrs}; +%% remove 'apply' instrs for upgrade, and collect the 'Extra' parameter +do_filter_and_get({apply, {emqx_relup, post_release_upgrade, [_, UpExtra0]}}, + {_, DnExtra, EmqxMods, RemainInstrs}) -> + {UpExtra0, DnExtra, EmqxMods, RemainInstrs}; +%% remove 'apply' instrs for downgrade, and collect the 'Extra' parameter +do_filter_and_get({apply, {emqx_relup, post_release_downgrade, [_, DnExtra0]}}, + {UpExtra, _, EmqxMods, RemainInstrs}) -> + {UpExtra, DnExtra0, EmqxMods, RemainInstrs}; +%% keep all other instrs unchanged +do_filter_and_get(Instr, {UpExtra, DnExtra, EmqxMods, RemainInstrs}) -> + {UpExtra, DnExtra, EmqxMods, RemainInstrs ++ [Instr]}. + assert_mandatory_modules(up, Mods) -> assert(lists:member(emqx_relup, Mods) andalso lists:member(emqx_app, Mods), - "cannot found 'load_module' instructions for emqx_app and emqx_rel: ~p", [Mods]); + "cannot find any 'load_object_code' instructions for emqx_app and emqx_rel: ~p", [Mods]); assert_mandatory_modules(down, Mods) -> assert(lists:member(emqx_app, Mods), - "cannot found 'load_module' instructions for emqx_app", []). + "cannot find any 'load_object_code' instructions for emqx_app", []). assert(true, _, _) -> ok; diff --git a/src/emqx_relup.erl b/src/emqx_relup.erl index dc443947a..fcc90d088 100644 --- a/src/emqx_relup.erl +++ b/src/emqx_relup.erl @@ -17,7 +17,7 @@ -module(emqx_relup). %% NOTE: DO NOT remove this `-include`. -%% We use this to forece this module to upgraded every release. +%% We use this to force this module to be upgraded every release. -include("emqx_release.hrl"). -export([ post_release_upgrade/2 @@ -27,13 +27,13 @@ -define(INFO(FORMAT), io:format("[emqx_relup] " ++ FORMAT ++ "~n")). -define(INFO(FORMAT, ARGS), io:format("[emqx_relup] " ++ FORMAT ++ "~n", ARGS)). -%% what to do after upgraded from a old release vsn. +%% What to do after upgraded from an old release vsn. post_release_upgrade(FromRelVsn, _) -> {_, CurrRelVsn} = ?EMQX_RELEASE, ?INFO("emqx has been upgraded to from ~s to ~s!", [FromRelVsn, CurrRelVsn]), reload_components(). -%% what to do after downgraded to a old release vsn. +%% What to do after downgraded to an old release vsn. post_release_downgrade(ToRelVsn, _) -> {_, CurrRelVsn} = ?EMQX_RELEASE, ?INFO("emqx has been downgraded to from ~s to ~s!", [CurrRelVsn, ToRelVsn]), From 8be8f538a101ab138d1e0a21822b52f0e653ab7b Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Wed, 2 Mar 2022 11:36:42 +0800 Subject: [PATCH 13/13] fix(relup): verify emqx_relup call in upgrade and downgrade clauses --- scripts/inject-relup.escript | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript index 32c944246..e2d3de795 100755 --- a/scripts/inject-relup.escript +++ b/scripts/inject-relup.escript @@ -46,8 +46,6 @@ inject_relup_instrs(Type, RUs) -> {Vsn, Desc, append_emqx_relup_instrs(Type, Vsn, Instrs)} end, RUs). -%% The `{apply, emqx_relup, post_release_upgrade, []}` will be appended to the end of -%% the instruction lists. append_emqx_relup_instrs(up, FromRelVsn, Instrs0) -> {{UpExtra, _}, Instrs1} = filter_and_check_instrs(up, Instrs0), Instrs1 ++ @@ -57,10 +55,10 @@ append_emqx_relup_instrs(up, FromRelVsn, Instrs0) -> ]; append_emqx_relup_instrs(down, ToRelVsn, Instrs0) -> - {{_, DnExtra}, Instrs1} = filter_and_check_instrs(down, Instrs0) + {{_, DnExtra}, Instrs1} = filter_and_check_instrs(down, Instrs0), %% NOTE: When downgrading, we apply emqx_relup:post_release_downgrade/2 before reloading %% or removing the emqx_relup module. - Instrs1 ++ + Instrs2 = Instrs1 ++ [ {load, {emqx_app, brutal_purge, soft_purge}} , {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, DnExtra]}} ], @@ -68,14 +66,24 @@ append_emqx_relup_instrs(down, ToRelVsn, Instrs0) -> LoadInsts = case ToRelVsn of ToRelVsn when ToRelVsn =:= "4.4.1"; ToRelVsn =:= "4.4.0" -> - [{remove, {emqx_relup, brutal_purge, brutal_purge}}]; + [ {remove, {emqx_relup, brutal_purge, brutal_purge}} + , {purge, [emqx_relup]} + ]; _ -> [{load, {emqx_relup, brutal_purge, soft_purge}}] end, - Instrs1 ++ LoadInsts. + Instrs2 ++ LoadInsts. filter_and_check_instrs(Type, Instrs) -> case filter_fetch_emqx_mods_and_extra(Instrs) of + {_, DnExtra, _, _} when Type =:= up, DnExtra =/= undefined -> + ?ERROR("got '{apply,{emqx_relup,post_release_downgrade,[_,Extra]}}'" + " from the upgrade instruction list, should be 'post_release_upgrade'", []), + error({instruction_not_found, load_object_code}); + {UpExtra, _, _, _} when Type =:= down, UpExtra =/= undefined -> + ?ERROR("got '{apply,{emqx_relup,post_release_upgrade,[_,Extra]}}'" + " from the downgrade instruction list, should be 'post_release_downgrade'", []), + error({instruction_not_found, load_object_code}); {_, _, [], _} -> ?ERROR("cannot find any 'load_object_code' instructions for app emqx", []), error({instruction_not_found, load_object_code}); @@ -85,19 +93,21 @@ filter_and_check_instrs(Type, Instrs) -> end. filter_fetch_emqx_mods_and_extra(Instrs) -> - lists:foldl(fun do_filter_and_get/2, {UpExtra, DnExtra, [], []}, Instrs). + lists:foldl(fun do_filter_and_get/2, {undefined, undefined, [], []}, Instrs). %% collect modules for emqx app do_filter_and_get({load_object_code, {emqx, _AppVsn, Mods}} = Instr, {UpExtra, DnExtra, EmqxMods, RemainInstrs}) -> {UpExtra, DnExtra, EmqxMods ++ Mods, RemainInstrs ++ [Instr]}; -%% remove 'load' instrs +%% remove 'load' instrs for emqx_relup and emqx_app do_filter_and_get({load, {Mod, _, _}}, {UpExtra, DnExtra, EmqxMods, RemainInstrs}) when Mod =:= emqx_relup; Mod =:= emqx_app -> {UpExtra, DnExtra, EmqxMods, RemainInstrs}; -%% remove 'remove' instrs +%% remove 'remove' and 'purge' instrs for emqx_relup do_filter_and_get({remove, {emqx_relup, _, _}}, {UpExtra, DnExtra, EmqxMods, RemainInstrs}) -> {UpExtra, DnExtra, EmqxMods, RemainInstrs}; +do_filter_and_get({purge, [emqx_relup]}, {UpExtra, DnExtra, EmqxMods, RemainInstrs}) -> + {UpExtra, DnExtra, EmqxMods, RemainInstrs}; %% remove 'apply' instrs for upgrade, and collect the 'Extra' parameter do_filter_and_get({apply, {emqx_relup, post_release_upgrade, [_, UpExtra0]}}, {_, DnExtra, EmqxMods, RemainInstrs}) ->