Просмотр исходного кода

fix: porting emqx_relup.erl from 4.4

Shawn 3 лет назад
Родитель
Сommit
3d7cab4288
3 измененных файлов с 168 добавлено и 3 удалено
  1. 42 0
      apps/emqx/src/emqx_relup.erl
  2. 5 3
      build
  3. 121 0
      scripts/inject-relup.escript

+ 42 - 0
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.

+ 5 - 3
build

@@ -175,18 +175,20 @@ 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)
+    ## 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}" -- *

+ 121 - 0
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() ++ " <path-to-relup-file>".
+
+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]).