diff --git a/.ci/fvt_tests/relup.lux b/.ci/fvt_tests/relup.lux index 4a8894476..45065d4bb 100644 --- a/.ci/fvt_tests/relup.lux +++ b/.ci/fvt_tests/relup.lux @@ -22,7 +22,8 @@ [shell emqx] !cd $PACKAGE_PATH - !tar -zxf ${PROFILE}-$(echo $old_vsn | sed -r 's/[v|e]//g')-*-ubuntu20.04-amd64.tar.gz + mkdir -p emqx + !tar -C emqx -zxf ${PROFILE}-$(echo $old_vsn | sed -r 's/[v|e]//g')-*-ubuntu20.04-amd64.tar.gz ?SH-PROMPT !cd emqx diff --git a/.github/workflows/build_packages.yaml b/.github/workflows/build_packages.yaml index 9bcfa1ced..09a443848 100644 --- a/.github/workflows/build_packages.yaml +++ b/.github/workflows/build_packages.yaml @@ -179,7 +179,8 @@ jobs: working-directory: source run: | pkg_name=$(find _packages/${{ matrix.profile }} -mindepth 1 -maxdepth 1 -iname \*.tar.gz) - tar -zxf $pkg_name + mkdir -p emqx + tar -C emqx -zxf $pkg_name # gsed -i '/emqx_telemetry/d' ./emqx/data/loaded_plugins ./emqx/bin/emqx start || cat emqx/log/erlang.log.1 ready='no' diff --git a/.github/workflows/build_slim_packages.yaml b/.github/workflows/build_slim_packages.yaml index e68c99cf4..f62febf0e 100644 --- a/.github/workflows/build_slim_packages.yaml +++ b/.github/workflows/build_slim_packages.yaml @@ -197,7 +197,8 @@ jobs: - name: test run: | pkg_name=$(find _packages/${{ matrix.profile }} -mindepth 1 -maxdepth 1 -iname \*.tar.gz) - tar -zxf $pkg_name + mkdir -p emqx + tar -C emqx -zxf $pkg_name # gsed -i '/emqx_telemetry/d' ./emqx/data/loaded_plugins ./emqx/bin/emqx start || cat emqx/log/erlang.log.1 ready='no' diff --git a/apps/emqx/src/emqx_relup.erl b/apps/emqx/src/emqx_relup.erl new file mode 100644 index 000000000..82ba90b4f --- /dev/null +++ b/apps/emqx/src/emqx_relup.erl @@ -0,0 +1,42 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2017-2022 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 force this module to be upgraded every release. +-include("emqx_release.hrl"). + +-export([ + post_release_upgrade/2, + 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 an old release vsn. +post_release_upgrade(FromRelVsn, _) -> + ?INFO("emqx has been upgraded from ~s to ~s!", [FromRelVsn, emqx_release:version()]), + reload_components(). + +%% What to do after downgraded to an old release vsn. +post_release_downgrade(ToRelVsn, _) -> + ?INFO("emqx has been downgraded from ~s to ~s!", [emqx_release:version(), ToRelVsn]), + reload_components(). + +reload_components() -> + ok. diff --git a/bin/install_upgrade.escript b/bin/install_upgrade.escript index 8adced930..4ab4947c0 100755 --- a/bin/install_upgrade.escript +++ b/bin/install_upgrade.escript @@ -4,7 +4,11 @@ %% ex: ft=erlang ts=4 sw=4 et -define(TIMEOUT, 300000). --define(INFO(Fmt,Args), io:format(Fmt++"~n",Args)). +-define(INFO(Fmt,Args), io:format(standard_io, Fmt++"~n",Args)). +-define(ERROR(Fmt,Args), io:format(standard_error, "ERROR: "++Fmt++"~n",Args)). +-define(SEMVER_RE, <<"^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[a-zA-Z\\d][-a-zA-Z.\\d]*)?(\\+[a-zA-Z\\d][-a-zA-Z.\\d]*)?$">>). + +-mode(compile). main([Command0, DistInfoStr | CommandArgs]) -> %% convert the distribution info arguments string to an erlang term @@ -52,6 +56,7 @@ unpack(_, Args) -> install({RelName, NameTypeArg, NodeName, Cookie}, Opts) -> TargetNode = start_distribution(NodeName, NameTypeArg, Cookie), Version = proplists:get_value(version, Opts), + validate_target_version(Version, TargetNode), case unpack_release(RelName, TargetNode, Version) of {ok, Vsn} -> ?INFO("Unpacked successfully: ~p.", [Vsn]), @@ -142,9 +147,10 @@ parse_arguments([VersionStr|Rest], Acc) -> parse_arguments(Rest, [{version, Version}] ++ Acc). unpack_release(RelName, TargetNode, Version) -> + StartScriptExists = filelib:is_dir(filename:join(["releases", Version, "start.boot"])), WhichReleases = which_releases(TargetNode), case proplists:get_value(Version, WhichReleases) of - undefined -> + Res when Res =:= undefined; (Res =:= unpacked andalso not StartScriptExists) -> %% not installed, so unpack tarball: %% look for a release package with the intended version in the following order: %% releases/-.tar.gz @@ -159,16 +165,43 @@ unpack_release(RelName, TargetNode, Version) -> case rpc:call(TargetNode, release_handler, unpack_release, [ReleasePackageLink], ?TIMEOUT) of {ok, Vsn} -> {ok, Vsn}; + {error, {existing_release, Vsn}} -> + %% sometimes the user may have removed the release/ dir + %% for an `unpacked` release, then we need to re-unpack it from + %% the .tar ball + untar_for_unpacked_release(str(RelName), Vsn), + {ok, Vsn}; {error, _} = Error -> Error end end; - Other -> Other + Other -> + Other end. -%% 1. look for a release package tarball with the provided version in the following order: -%% releases/-.tar.gz -%% releases//-.tar.gz -%% releases//.tar.gz +untar_for_unpacked_release(RelName, Vsn) -> + {ok, Root} = file:get_cwd(), + RelDir = filename:join([Root, "releases"]), + %% untar the .tar file, so release/ will be created + Tar = filename:join([RelDir, Vsn, RelName ++ ".tar.gz"]), + extract_tar(Root, Tar), + + %% create RELEASE file + RelFile = filename:join([RelDir, Vsn, RelName ++ ".rel"]), + release_handler:create_RELEASES(Root, RelFile), + + %% Clean release + _ = file:delete(Tar), + _ = file:delete(RelFile). + +extract_tar(Cwd, Tar) -> + case erl_tar:extract(Tar, [keep_old_files, {cwd, Cwd}, compressed]) of + ok -> ok; + {error, {Name, Reason}} -> % New erl_tar (R3A). + throw({error, {cannot_extract_file, Name, Reason}}) + end. + +%% 1. look for a release package tarball with the provided version: +%% releases/-**.tar.gz %% 2. create a symlink from a fixed location (ie. releases//.tar.gz) %% to the release package tarball found in 1. %% 3. return a tuple with the paths to the release package and @@ -184,47 +217,38 @@ find_and_link_release_package(Version, RelName) -> %% we've found where the actual release package is located ReleaseLink = filename:join(["releases", Version, RelNameStr ++ ".tar.gz"]), - TarBalls = [ - filename:join(["releases", - RelNameStr ++ "-" ++ Version ++ ".tar.gz"]), - filename:join(["releases", Version, - RelNameStr ++ "-" ++ Version ++ ".tar.gz"]), - filename:join(["releases", Version, - RelNameStr ++ ".tar.gz"]) - ], - case first_value(fun filelib:is_file/1, TarBalls) of - no_value -> + TarBalls = filename:join(["releases", RelNameStr ++ "-*" ++ Version ++ "*.tar.gz"]), + case filelib:wildcard(TarBalls) of + [] -> {undefined, undefined}; - %% no need to create the link since the release package we - %% found is located in the same place as the link would be - {ok, Filename} when is_list(Filename) andalso - Filename =:= ReleaseLink -> - {Filename, ReleaseHandlerPackageLink}; - {ok, Filename} when is_list(Filename) -> - %% we now have the location of the release package, however - %% release handler expects a fixed nomenclature (.tar.gz) + [Filename] when is_list(Filename) -> + %% the release handler expects a fixed nomenclature (.tar.gz) %% so give it just that by creating a symlink to the tarball %% we found. %% make sure that the dir where we're creating the link in exists ok = filelib:ensure_dir(filename:join([filename:dirname(ReleaseLink), "dummy"])), %% create the symlink pointing to the full path name of the %% release package we found - case file:make_symlink(filename:absname(Filename), ReleaseLink) of - ok -> - ok; - {error, eperm} -> % windows! - {ok,_} = file:copy(filename:absname(Filename), ReleaseLink) - end, - {Filename, ReleaseHandlerPackageLink} + make_symlink_or_copy(filename:absname(Filename), ReleaseLink), + {Filename, ReleaseHandlerPackageLink}; + Files -> + ?ERROR("Found more than one package for version: '~s', " + "files: ~p", [Version, Files]), + erlang:halt(47) end. -first_value(_Fun, []) -> no_value; -first_value(Fun, [Value | Rest]) -> - case Fun(Value) of - false -> - first_value(Fun, Rest); - true -> - {ok, Value} +make_symlink_or_copy(Filename, ReleaseLink) -> + case file:make_symlink(Filename, ReleaseLink) of + ok -> ok; + {error, eexist} -> + ?INFO("Symlink ~p already exists, recreate it", [ReleaseLink]), + ok = file:delete(ReleaseLink), + make_symlink_or_copy(Filename, ReleaseLink); + {error, Reason} when Reason =:= eperm; Reason =:= enotsup -> + {ok, _} = file:copy(Filename, ReleaseLink); + {error, Reason} -> + ?ERROR("Create symlink ~p failed, error: ~p", [ReleaseLink, Reason]), + erlang:halt(47) end. parse_version(V) when is_list(V) -> @@ -246,7 +270,7 @@ check_and_install(TargetNode, Vsn) -> {ok, _OtherVsn, _Desc} -> ok; {error, Reason} -> - ?INFO("ERROR: release_handler:check_install_release failed: ~p.",[Reason]), + ?ERROR("Call release_handler:check_install_release failed: ~p.", [Reason]), erlang:halt(3) end, case rpc:call(TargetNode, release_handler, install_release, @@ -259,18 +283,18 @@ check_and_install(TargetNode, Vsn) -> iolist_to_binary( [io_lib:format("* ~s\t~s~n",[V,S]) || {V,S} <- which_releases(TargetNode)]), ?INFO("Installed versions:~n~s", [VerList]), - ?INFO("ERROR: Unable to revert to '~s' - not installed.", [Vsn]), + ?ERROR("Unable to revert to '~s' - not installed.", [Vsn]), erlang:halt(2); %% as described in http://erlang.org/doc/man/appup.html, when performing a relup %% with soft purge: %% If the value is soft_purge, release_handler:install_release/1 %% returns {error,{old_processes,Mod}} {error, {old_processes, Mod}} -> - ?INFO("ERROR: unable to install '~s' - old processes still running code from module ~p", + ?ERROR("Unable to install '~s' - old processes still running code from module ~p", [Vsn, Mod]), erlang:halt(3); {error, Reason1} -> - ?INFO("ERROR: release_handler:install_release failed: ~p",[Reason1]), + ?ERROR("Call release_handler:install_release failed: ~p",[Reason1]), erlang:halt(4) end. @@ -287,8 +311,7 @@ permafy(TargetNode, RelName, Vsn) -> make_permanent, [Vsn], ?TIMEOUT), ?INFO("Made release permanent: ~p", [Vsn]), %% upgrade/downgrade the scripts by replacing them - Scripts = [RelNameStr, RelNameStr ++ "_ctl", - "nodetool", "install_upgrade.escript"], + Scripts = [RelNameStr, RelNameStr++"_ctl", "nodetool", "install_upgrade.escript"], [{ok, _} = file:copy(filename:join(["bin", File++"-"++Vsn]), filename:join(["bin", File])) || File <- Scripts], @@ -302,7 +325,7 @@ remove_release(TargetNode, Vsn) -> ?INFO("Uninstalled Release: ~s", [Vsn]), ok; {error, Reason} -> - ?INFO("ERROR: release_handler:remove_release failed: ~p", [Reason]), + ?ERROR("Call release_handler:remove_release failed: ~p", [Reason]), erlang:halt(3) end. @@ -330,8 +353,7 @@ start_distribution(TargetNode, NameTypeArg, Cookie) -> MyNode = make_script_node(TargetNode), {ok, _Pid} = net_kernel:start([MyNode, get_name_type(NameTypeArg)]), erlang:set_cookie(node(), Cookie), - case {net_kernel:connect_node(TargetNode), - net_adm:ping(TargetNode)} of + case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of {true, pong} -> ok; {_, pang} -> @@ -344,7 +366,7 @@ start_distribution(TargetNode, NameTypeArg, Cookie) -> make_script_node(Node) -> [Name, Host] = string:tokens(atom_to_list(Node), "@"), - list_to_atom(lists:concat(["remsh_" ++ Name, "_upgrader_", os:getpid(), "@", Host])). + list_to_atom(lists:concat(["remsh_", Name, "_upgrader_", os:getpid(), "@", Host])). %% get name type from arg get_name_type(NameTypeArg) -> @@ -359,3 +381,35 @@ erts_vsn() -> {ok, Str} = file:read_file(filename:join(["releases", "start_erl.data"])), [ErtsVsn, _] = string:tokens(binary_to_list(Str), " "), ErtsVsn. + +validate_target_version(TargetVersion, TargetNode) -> + CurrentVersion = current_release_version(TargetNode), + case {get_major_minor_vsn(CurrentVersion), get_major_minor_vsn(TargetVersion)} of + {{Major, Minor}, {Major, Minor}} -> ok; + _ -> + ?ERROR("Cannot upgrade/downgrade from '~s' to '~s'~n" + "Hot upgrade is only supported between patch releases.", + [CurrentVersion, TargetVersion]), + erlang:halt(48) + end. + +get_major_minor_vsn(Version) -> + Parts = parse_semver(Version), + [Major | Rem0] = Parts, + [Minor | _Rem1] = Rem0, + {Major, Minor}. + +parse_semver(Version) -> + case re:run(Version, ?SEMVER_RE, [{capture, all_but_first, binary}]) of + {match, Parts} -> Parts; + nomatch -> + ?ERROR("Invalid semantic version: '~s'~n", [Version]), + erlang:halt(22) + end. + +str(A) when is_atom(A) -> + atom_to_list(A); +str(A) when is_binary(A) -> + binary_to_list(A); +str(A) when is_list(A) -> + (A). diff --git a/build b/build index bd60f1c5e..b48b943ee 100755 --- a/build +++ b/build @@ -108,15 +108,19 @@ make_elixir_rel() { ## extract previous version .tar.gz files to _build/$PROFILE/rel/emqx before making relup make_relup() { local rel_dir="_build/$PROFILE/rel/emqx" - mkdir -p "${rel_dir}/lib" - mkdir -p "${rel_dir}/releases" local name_pattern - name_pattern="${PROFILE}-$(./pkg-vsn.sh "$PROFILE" --vsn_matcher)" + name_pattern="${PROFILE}-$(./pkg-vsn.sh "$PROFILE" --vsn_matcher --long)" local releases=() while read -r tgzfile ; do local base_vsn - base_vsn="$(echo "$tgzfile" | grep -oE "[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta)\.[0-9])?(-[0-9a-f]{8})?" | head -1)" - tar -C "$rel_dir" -zxf ---keep-old-files "$tgzfile" emqx/releases emqx/lib + base_vsn="$(echo "$tgzfile" | grep -oE "[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta|rc)\.[0-9])?(-[0-9a-f]{8})?" | head -1)" + ## we have to create tmp dir to untar old tgz, as `tar --skip-old-files` is not supported on all plantforms + local tmp_dir + tmp_dir="$(mktemp -d -t emqx.XXXXXXX)" + tar -C "$tmp_dir" -zxf "$tgzfile" + cp -npr "$tmp_dir/releases"/* "${rel_dir}/releases/" || true + cp -npr "$tmp_dir/lib"/* "${rel_dir}/lib/" || true + rm -rf "$tmp_dir" releases+=( "$base_vsn" ) done < <("$FIND" _upgrade_base -maxdepth 1 -name "${name_pattern}.tar.gz" -type f) if [ ${#releases[@]} -eq 0 ]; then @@ -171,20 +175,23 @@ make_tgz() { target="${pkgpath}/${target_name}" src_tarball="${relpath}/emqx-${PKG_VSN}.tar.gz" - tard="tmp/emqx_untar_${PKG_VSN}" - rm -rf "${tard}" + tard="$(mktemp -d -t emqx.XXXXXXX)" mkdir -p "${tard}/emqx" mkdir -p "${pkgpath}" if [ ! -f "$src_tarball" ]; then log "ERROR: $src_tarball is not found" fi tar zxf "${src_tarball}" -C "${tard}/emqx" + if [ -f "${tard}/emqx/releases/${PKG_VSN}/relup" ]; then + ./scripts/inject-relup.escript "${tard}/emqx/releases/${PKG_VSN}/relup" + fi ## try to be portable for tar.gz packages. ## for DEB and RPM packages the dependencies are resoved by yum and apt cp_dyn_libs "${tard}/emqx" - ## create tar after change dir (for windows) - pushd "${tard}" >/dev/null - tar -czf "${target_name}" emqx + ## create tar after change dir + ## to avoid creating an extra level of 'emqx' dir in the .tar.gz file + pushd "${tard}/emqx" >/dev/null + tar -zcf "../${target_name}" -- * popd >/dev/null mv "${tard}/${target_name}" "${target}" case "$SYSTEM" in diff --git a/scripts/inject-relup.escript b/scripts/inject-relup.escript new file mode 100755 index 000000000..b7d905979 --- /dev/null +++ b/scripts/inject-relup.escript @@ -0,0 +1,121 @@ +#!/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([RelupFile]) -> + ok = inject_relup_file(RelupFile); +main(_Args) -> + ?ERROR("~s", [usage()]), + erlang:halt(1). + +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, UpVsnRUs), + inject_relup_instrs(down, 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("Cannot find relup file: ~p", [File]), + ok; + {error, Reason} -> + ?ERROR("Read relup file ~p failed: ~p", [File, Reason]), + error({read_relup_error, Reason}) + end. + +inject_relup_instrs(Type, RUs) -> + lists:map(fun({Vsn, Desc, Instrs}) -> + {Vsn, Desc, append_emqx_relup_instrs(Type, Vsn, Instrs)} + end, RUs). + +append_emqx_relup_instrs(up, FromRelVsn, Instrs0) -> + {{UpExtra, _}, Instrs1} = filter_and_check_instrs(up, Instrs0), + Instrs1 ++ + [ {load, {emqx_release, brutal_purge, soft_purge}} + , {load, {emqx_relup, brutal_purge, soft_purge}} + , {apply, {emqx_relup, post_release_upgrade, [FromRelVsn, UpExtra]}} + ]; + +append_emqx_relup_instrs(down, ToRelVsn, 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. + Instrs2 = Instrs1 ++ + [ {load, {emqx_release, brutal_purge, soft_purge}} + , {apply, {emqx_relup, post_release_downgrade, [ToRelVsn, DnExtra]}} + , {load, {emqx_relup, brutal_purge, soft_purge}} + ], + Instrs2. + +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}); + {UpExtra, DnExtra, EmqxMods, RemainInstrs} -> + assert_mandatory_modules(Type, EmqxMods), + {{UpExtra, DnExtra}, RemainInstrs} + end. + +filter_fetch_emqx_mods_and_extra(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 for emqx_relup and emqx_release +do_filter_and_get({load, {Mod, _, _}}, {UpExtra, DnExtra, EmqxMods, RemainInstrs}) + when Mod =:= emqx_relup; Mod =:= emqx_release -> + {UpExtra, DnExtra, EmqxMods, RemainInstrs}; +%% 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}) -> + {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(_, Mods) -> + MandInstrs = [{load_module,emqx_release,brutal_purge,soft_purge,[]}, + {load_module,emqx_relup}], + assert(lists:member(emqx_relup, Mods) andalso lists:member(emqx_release, Mods), + "The following instructions are mandatory in every clause of the emqx.appup.src: ~p", [MandInstrs]). + +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/scripts/pkg-tests.sh b/scripts/pkg-tests.sh index 81800427c..5fce0f71e 100755 --- a/scripts/pkg-tests.sh +++ b/scripts/pkg-tests.sh @@ -88,7 +88,8 @@ emqx_test(){ local packagename="${PACKAGE_FILE_NAME}" case "$PKG_SUFFIX" in "tar.gz") - tar -zxf "${PACKAGE_PATH}/${packagename}" + mkdir -p "${PACKAGE_PATH}/emqx" + tar -C "${PACKAGE_PATH}/emqx" -zxf "${PACKAGE_PATH}/${packagename}" export EMQX_ZONES__DEFAULT__MQTT__SERVER_KEEPALIVE=60 export EMQX_MQTT__MAX_TOPIC_ALIAS=10 export EMQX_LOG__CONSOLE_HANDLER__LEVEL=debug @@ -231,7 +232,8 @@ relup_test(){ pattern="$EMQX_NAME-$("$CODE_PATH"/pkg-vsn.sh "${EMQX_NAME}" --long --vsn_matcher)" while read -r pkg; do packagename=$(basename "${pkg}") - tar -zxf "$packagename" + mkdir -p emqx + tar -C emqx -zxf "$packagename" if ! ./emqx/bin/emqx start; then cat emqx/log/erlang.log.1 || true cat emqx/log/emqx.log.1 || true diff --git a/scripts/update_appup.escript b/scripts/update_appup.escript index f2b6235e9..21fc9bb34 100755 --- a/scripts/update_appup.escript +++ b/scripts/update_appup.escript @@ -173,7 +173,7 @@ download_prev_release(Tag, #{binary_rel_url := {ok, URL0}, clone_url := Repo}) - Filename = filename:join(BaseDir, Dir), Script = "mkdir -p ${OUTFILE} && wget -c -O ${OUTFILE}.tar.gz ${URL} && - tar -zxf ${OUTFILE} ${OUTFILE}.tar.gz", + tar -zxf ${OUTFILE}.tar.gz -C ${OUTFILE}", Env = [{"TAG", Tag}, {"OUTFILE", Filename}, {"URL", URL}], bash(Script, Env), {ok, Filename}.