| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- #!/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]).
|