Merge pull request #13114 from emqx/emqx-relup-gen
feat: generate relup tarball, add relup APIs
This commit is contained in:
commit
7bb7b10a31
4
Makefile
4
Makefile
|
@ -244,7 +244,7 @@ $(foreach zt,$(ALL_ZIPS),$(eval $(call download-relup-packages,$(zt))))
|
||||||
## relup target is to create relup instructions
|
## relup target is to create relup instructions
|
||||||
.PHONY: $(REL_PROFILES:%=%-relup)
|
.PHONY: $(REL_PROFILES:%=%-relup)
|
||||||
define gen-relup-target
|
define gen-relup-target
|
||||||
$1-relup: $1-relup-downloads $(COMMON_DEPS)
|
$1-relup: $(COMMON_DEPS)
|
||||||
@$(BUILD) $1 relup
|
@$(BUILD) $1 relup
|
||||||
endef
|
endef
|
||||||
ALL_TGZS = $(REL_PROFILES)
|
ALL_TGZS = $(REL_PROFILES)
|
||||||
|
@ -253,7 +253,7 @@ $(foreach zt,$(ALL_TGZS),$(eval $(call gen-relup-target,$(zt))))
|
||||||
## tgz target is to create a release package .tar.gz with relup
|
## tgz target is to create a release package .tar.gz with relup
|
||||||
.PHONY: $(REL_PROFILES:%=%-tgz)
|
.PHONY: $(REL_PROFILES:%=%-tgz)
|
||||||
define gen-tgz-target
|
define gen-tgz-target
|
||||||
$1-tgz: $1-relup
|
$1-tgz: $(COMMON_DEPS)
|
||||||
@$(BUILD) $1 tgz
|
@$(BUILD) $1 tgz
|
||||||
endef
|
endef
|
||||||
ALL_TGZS = $(REL_PROFILES)
|
ALL_TGZS = $(REL_PROFILES)
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
{emqx_mgmt_api_plugins,1}.
|
{emqx_mgmt_api_plugins,1}.
|
||||||
{emqx_mgmt_api_plugins,2}.
|
{emqx_mgmt_api_plugins,2}.
|
||||||
{emqx_mgmt_api_plugins,3}.
|
{emqx_mgmt_api_plugins,3}.
|
||||||
|
{emqx_mgmt_api_relup,1}.
|
||||||
{emqx_mgmt_cluster,1}.
|
{emqx_mgmt_cluster,1}.
|
||||||
{emqx_mgmt_cluster,2}.
|
{emqx_mgmt_cluster,2}.
|
||||||
{emqx_mgmt_cluster,3}.
|
{emqx_mgmt_cluster,3}.
|
||||||
|
|
|
@ -173,7 +173,9 @@
|
||||||
system_code_change/4
|
system_code_change/4
|
||||||
]}
|
]}
|
||||||
).
|
).
|
||||||
|
-dialyzer({no_missing_calls, [handle_msg/2]}).
|
||||||
|
|
||||||
|
-ifndef(BUILD_WITHOUT_QUIC).
|
||||||
-spec start_link
|
-spec start_link
|
||||||
(esockd:transport(), esockd:socket(), emqx_channel:opts()) ->
|
(esockd:transport(), esockd:socket(), emqx_channel:opts()) ->
|
||||||
{ok, pid()};
|
{ok, pid()};
|
||||||
|
@ -183,6 +185,9 @@
|
||||||
emqx_quic_connection:cb_state()
|
emqx_quic_connection:cb_state()
|
||||||
) ->
|
) ->
|
||||||
{ok, pid()}.
|
{ok, pid()}.
|
||||||
|
-else.
|
||||||
|
-spec start_link(esockd:transport(), esockd:socket(), emqx_channel:opts()) -> {ok, pid()}.
|
||||||
|
-endif.
|
||||||
|
|
||||||
start_link(Transport, Socket, Options) ->
|
start_link(Transport, Socket, Options) ->
|
||||||
Args = [self(), Transport, Socket, Options],
|
Args = [self(), Transport, Socket, Options],
|
||||||
|
|
|
@ -64,6 +64,17 @@
|
||||||
|
|
||||||
-export_type([listener_id/0]).
|
-export_type([listener_id/0]).
|
||||||
|
|
||||||
|
-dialyzer(
|
||||||
|
{no_unknown, [
|
||||||
|
is_running/3,
|
||||||
|
current_conns/3,
|
||||||
|
do_stop_listener/3,
|
||||||
|
do_start_listener/4,
|
||||||
|
do_update_listener/4,
|
||||||
|
quic_listener_conf_rollback/3
|
||||||
|
]}
|
||||||
|
).
|
||||||
|
|
||||||
-type listener_id() :: atom() | binary().
|
-type listener_id() :: atom() | binary().
|
||||||
-type listener_type() :: tcp | ssl | ws | wss | quic | dtls.
|
-type listener_type() :: tcp | ssl | ws | wss | quic | dtls.
|
||||||
|
|
||||||
|
@ -1018,7 +1029,6 @@ ensure_max_conns(<<"infinity">>) -> <<"infinity">>;
|
||||||
ensure_max_conns(MaxConn) when is_binary(MaxConn) -> binary_to_integer(MaxConn);
|
ensure_max_conns(MaxConn) when is_binary(MaxConn) -> binary_to_integer(MaxConn);
|
||||||
ensure_max_conns(MaxConn) -> MaxConn.
|
ensure_max_conns(MaxConn) -> MaxConn.
|
||||||
|
|
||||||
-spec quic_listen_on(X :: any()) -> quicer:listen_on().
|
|
||||||
quic_listen_on(Bind) ->
|
quic_listen_on(Bind) ->
|
||||||
case Bind of
|
case Bind of
|
||||||
{Addr, Port} when tuple_size(Addr) == 4 ->
|
{Addr, Port} when tuple_size(Addr) == 4 ->
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2017-2024 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_post_upgrade).
|
||||||
|
|
||||||
|
%% Example of a hot upgrade callback function.
|
||||||
|
%% PR#12765
|
||||||
|
% -export([
|
||||||
|
% pr12765_update_stats_timer/1,
|
||||||
|
% pr20000_ensure_sup_started/3
|
||||||
|
% ]).
|
||||||
|
|
||||||
|
%% Please ensure that every callback function is reentrant.
|
||||||
|
%% This way, users can attempt upgrade multiple times if an issue arises.
|
||||||
|
%%
|
||||||
|
% pr12765_update_stats_timer(_FromVsn) ->
|
||||||
|
% emqx_stats:update_interval(broker_stats, fun emqx_broker_helper:stats_fun/0).
|
||||||
|
%
|
||||||
|
% pr20000_ensure_sup_started(_FromVsn, "5.6.1" ++ _, ChildSpec) ->
|
||||||
|
% ChildId = maps:get(id, ChildSpec),
|
||||||
|
% case supervisor:terminate_child(emqx_sup, ChildId) of
|
||||||
|
% ok -> supervisor:delete_child(emqx_sup, ChildId);
|
||||||
|
% Error -> Error
|
||||||
|
% end,
|
||||||
|
% supervisor:start_child(emqx_sup, ChildSpec);
|
||||||
|
% pr20000_ensure_sup_started(_FromVsn, _TargetVsn, _) ->
|
||||||
|
% ok.
|
|
@ -1,42 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2017-2024 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.
|
|
|
@ -1,6 +1,6 @@
|
||||||
{application, emqx_bridge_sqlserver, [
|
{application, emqx_bridge_sqlserver, [
|
||||||
{description, "EMQX Enterprise SQL Server Bridge"},
|
{description, "EMQX Enterprise SQL Server Bridge"},
|
||||||
{vsn, "0.2.2"},
|
{vsn, "0.2.3"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications, [kernel, stdlib, emqx_resource, odbc]},
|
{applications, [kernel, stdlib, emqx_resource, odbc]},
|
||||||
{env, [
|
{env, [
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
%% https://www.erlang.org/doc/man/odbc.html
|
%% https://www.erlang.org/doc/man/odbc.html
|
||||||
|
|
||||||
%% as returned by connect/2
|
%% as returned by connect/2
|
||||||
-type connection_reference() :: odbc:connection_reference().
|
-type connection_reference() :: term().
|
||||||
-type time_out() :: milliseconds() | infinity.
|
-type time_out() :: milliseconds() | infinity.
|
||||||
-type sql() :: string() | binary().
|
-type sql() :: string() | binary().
|
||||||
-type milliseconds() :: pos_integer().
|
-type milliseconds() :: pos_integer().
|
||||||
|
|
|
@ -566,6 +566,8 @@ install_package(FileName, Bin) ->
|
||||||
ok = filelib:ensure_dir(File),
|
ok = filelib:ensure_dir(File),
|
||||||
ok = file:write_file(File, Bin),
|
ok = file:write_file(File, Bin),
|
||||||
PackageName = string:trim(FileName, trailing, ".tar.gz"),
|
PackageName = string:trim(FileName, trailing, ".tar.gz"),
|
||||||
|
MD5 = emqx_utils:bin_to_hexstr(crypto:hash(md5, Bin), lower),
|
||||||
|
ok = file:write_file(emqx_plugins:md5sum_file(PackageName), MD5),
|
||||||
case emqx_plugins:ensure_installed(PackageName, ?fresh_install) of
|
case emqx_plugins:ensure_installed(PackageName, ?fresh_install) of
|
||||||
{error, #{reason := plugin_not_found}} = NotFound ->
|
{error, #{reason := plugin_not_found}} = NotFound ->
|
||||||
NotFound;
|
NotFound;
|
||||||
|
@ -596,6 +598,7 @@ delete_package(Name, _Opts) ->
|
||||||
_ = emqx_plugins:ensure_disabled(Name),
|
_ = emqx_plugins:ensure_disabled(Name),
|
||||||
_ = emqx_plugins:ensure_uninstalled(Name),
|
_ = emqx_plugins:ensure_uninstalled(Name),
|
||||||
_ = emqx_plugins:delete_package(Name),
|
_ = emqx_plugins:delete_package(Name),
|
||||||
|
_ = file:delete(emqx_plugins:md5sum_file(Name)),
|
||||||
ok;
|
ok;
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
|
|
|
@ -0,0 +1,668 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2020-2024 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_mgmt_api_relup).
|
||||||
|
|
||||||
|
-behaviour(minirest_api).
|
||||||
|
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
|
-export([get_upgrade_status/0, emqx_relup_upgrade/1]).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
api_spec/0,
|
||||||
|
fields/1,
|
||||||
|
paths/0,
|
||||||
|
schema/1,
|
||||||
|
namespace/0,
|
||||||
|
validate_name/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
'/relup/package/upload'/2,
|
||||||
|
'/relup/package'/2,
|
||||||
|
'/relup/status'/2,
|
||||||
|
'/relup/status/:node'/2,
|
||||||
|
'/relup/upgrade'/2,
|
||||||
|
'/relup/upgrade/:node'/2
|
||||||
|
]).
|
||||||
|
|
||||||
|
-ignore_xref(emqx_relup_main).
|
||||||
|
|
||||||
|
-define(TAGS, [<<"Relup">>]).
|
||||||
|
-define(NAME_RE, "^[A-Za-z]+[A-Za-z0-9-_.]*$").
|
||||||
|
-define(CONTENT_PACKAGE, plugin).
|
||||||
|
-define(PLUGIN_NAME, <<"emqx_relup">>).
|
||||||
|
|
||||||
|
-define(EXAM_VSN1, <<"5.8.0">>).
|
||||||
|
-define(EXAM_VSN2, <<"5.8.1">>).
|
||||||
|
-define(EXAM_VSN3, <<"5.8.2">>).
|
||||||
|
-define(EXAM_PACKAGE_NAME_2, <<"emqx_relup-5.8.1.tar.gz">>).
|
||||||
|
-define(EXAM_PACKAGE_NAME_3, <<"emqx_relup-5.8.2.tar.gz">>).
|
||||||
|
|
||||||
|
-define(ASSERT_PKG_READY(EXPR),
|
||||||
|
case code:is_loaded(emqx_relup_main) of
|
||||||
|
false -> return_package_not_installed();
|
||||||
|
{file, _} -> EXPR
|
||||||
|
end
|
||||||
|
).
|
||||||
|
|
||||||
|
%%==============================================================================
|
||||||
|
%% API Spec
|
||||||
|
namespace() ->
|
||||||
|
"relup".
|
||||||
|
|
||||||
|
api_spec() ->
|
||||||
|
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
||||||
|
|
||||||
|
paths() ->
|
||||||
|
[
|
||||||
|
"/relup/package",
|
||||||
|
"/relup/package/upload",
|
||||||
|
"/relup/status",
|
||||||
|
"/relup/status/:node",
|
||||||
|
"/relup/upgrade",
|
||||||
|
"/relup/upgrade/:node"
|
||||||
|
].
|
||||||
|
|
||||||
|
schema("/relup/package/upload") ->
|
||||||
|
#{
|
||||||
|
'operationId' => '/relup/package/upload',
|
||||||
|
post => #{
|
||||||
|
summary => <<"Upload a hot upgrade package">>,
|
||||||
|
description => <<
|
||||||
|
"Upload a hot upgrade package (emqx_relup-vsn.tar.gz).<br/>"
|
||||||
|
"Note that only one package is alllowed to be installed at a time."
|
||||||
|
>>,
|
||||||
|
tags => ?TAGS,
|
||||||
|
'requestBody' => #{
|
||||||
|
content => #{
|
||||||
|
'multipart/form-data' => #{
|
||||||
|
schema => #{
|
||||||
|
type => object,
|
||||||
|
properties => #{
|
||||||
|
?CONTENT_PACKAGE => #{type => string, format => binary}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
encoding => #{?CONTENT_PACKAGE => #{'contentType' => 'application/gzip'}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses => #{
|
||||||
|
204 => <<"Package is uploaded successfully">>,
|
||||||
|
400 => emqx_dashboard_swagger:error_codes(
|
||||||
|
['UNEXPECTED_ERROR', 'ALREADY_INSTALLED', 'BAD_PLUGIN_INFO']
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/relup/package") ->
|
||||||
|
#{
|
||||||
|
'operationId' => '/relup/package',
|
||||||
|
get => #{
|
||||||
|
summary => <<"Get the installed hot upgrade package">>,
|
||||||
|
description =>
|
||||||
|
<<"Get information of the installed hot upgrade package.<br/>">>,
|
||||||
|
tags => ?TAGS,
|
||||||
|
responses => #{
|
||||||
|
200 => hoconsc:ref(package),
|
||||||
|
404 => emqx_dashboard_swagger:error_codes(
|
||||||
|
['NOT_FOUND'],
|
||||||
|
<<"No relup package is installed">>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete => #{
|
||||||
|
summary => <<"Delete the installed hot upgrade package">>,
|
||||||
|
description =>
|
||||||
|
<<"Delete the installed hot upgrade package.<br/>">>,
|
||||||
|
tags => ?TAGS,
|
||||||
|
responses => #{
|
||||||
|
204 => <<"Packages are deleted successfully">>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/relup/status") ->
|
||||||
|
#{
|
||||||
|
'operationId' => '/relup/status',
|
||||||
|
get => #{
|
||||||
|
summary => <<"Get the hot upgrade status of all nodes">>,
|
||||||
|
description => <<"Get the hot upgrade status of all nodes">>,
|
||||||
|
tags => ?TAGS,
|
||||||
|
responses => #{
|
||||||
|
200 => hoconsc:array(hoconsc:ref(running_status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/relup/status/:node") ->
|
||||||
|
#{
|
||||||
|
'operationId' => '/relup/status/:node',
|
||||||
|
get => #{
|
||||||
|
summary => <<"Get the hot upgrade status of a specified node">>,
|
||||||
|
description => <<"Get the hot upgrade status of a specified node">>,
|
||||||
|
tags => ?TAGS,
|
||||||
|
parameters => [hoconsc:ref(node_name)],
|
||||||
|
responses => #{
|
||||||
|
200 => hoconsc:ref(running_status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/relup/upgrade") ->
|
||||||
|
#{
|
||||||
|
'operationId' => '/relup/upgrade',
|
||||||
|
post => #{
|
||||||
|
summary => <<"Upgrade all nodes">>,
|
||||||
|
description => <<
|
||||||
|
"Upgrade all nodes to the target version with the installed package."
|
||||||
|
>>,
|
||||||
|
tags => ?TAGS,
|
||||||
|
responses => #{
|
||||||
|
204 => <<"Upgrade is started successfully">>,
|
||||||
|
400 => emqx_dashboard_swagger:error_codes(
|
||||||
|
['UNEXPECTED_ERROR'],
|
||||||
|
<<"Upgrade failed because of invalid input or environment">>
|
||||||
|
),
|
||||||
|
500 => emqx_dashboard_swagger:error_codes(
|
||||||
|
['INTERNAL_ERROR'], <<"Upgrade failed because of internal errors">>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/relup/upgrade/:node") ->
|
||||||
|
#{
|
||||||
|
'operationId' => '/relup/upgrade/:node',
|
||||||
|
post => #{
|
||||||
|
summary => <<"Upgrade a specified node">>,
|
||||||
|
description => <<
|
||||||
|
"Upgrade a specified node to the target version with the installed package."
|
||||||
|
>>,
|
||||||
|
tags => ?TAGS,
|
||||||
|
parameters => [hoconsc:ref(node_name)],
|
||||||
|
responses => #{
|
||||||
|
204 => <<"Upgrade is started successfully">>,
|
||||||
|
400 => emqx_dashboard_swagger:error_codes(
|
||||||
|
['UNEXPECTED_ERROR'],
|
||||||
|
<<"Upgrade failed because of invalid input or environment">>
|
||||||
|
),
|
||||||
|
404 => emqx_dashboard_swagger:error_codes(
|
||||||
|
['NOT_FOUND'],
|
||||||
|
<<"Node not found">>
|
||||||
|
),
|
||||||
|
500 => emqx_dashboard_swagger:error_codes(
|
||||||
|
['INTERNAL_ERROR'], <<"Upgrade failed because of internal errors">>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
|
%%==============================================================================
|
||||||
|
%% Field definitions
|
||||||
|
fields(package) ->
|
||||||
|
[
|
||||||
|
{name,
|
||||||
|
hoconsc:mk(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
desc => <<"File name of the package">>,
|
||||||
|
validator => fun ?MODULE:validate_name/1,
|
||||||
|
example => ?EXAM_PACKAGE_NAME_3
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{target_vsn,
|
||||||
|
hoconsc:mk(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
desc => <<"Target emqx version for this package">>,
|
||||||
|
example => ?EXAM_VSN3
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{built_on_otp_release, hoconsc:mk(binary(), #{example => <<"24">>})},
|
||||||
|
{applicable_vsns,
|
||||||
|
hoconsc:mk(hoconsc:array(binary()), #{
|
||||||
|
example => [?EXAM_VSN1, ?EXAM_VSN2],
|
||||||
|
desc => <<"The emqx versions that this package can be applied to.">>
|
||||||
|
})},
|
||||||
|
{build_date,
|
||||||
|
hoconsc:mk(binary(), #{
|
||||||
|
example => <<"2021-12-25">>,
|
||||||
|
desc => <<"The date when the package was built.">>
|
||||||
|
})},
|
||||||
|
{change_logs,
|
||||||
|
hoconsc:mk(
|
||||||
|
hoconsc:array(binary()),
|
||||||
|
#{
|
||||||
|
desc => <<"Changes that this package brings">>,
|
||||||
|
example => [
|
||||||
|
<<
|
||||||
|
"1. Fix a bug foo in the plugin."
|
||||||
|
"2. Add a new bar feature."
|
||||||
|
>>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{md5_sum, hoconsc:mk(binary(), #{example => <<"d41d8cd98f00b204e9800998ecf8427e">>})}
|
||||||
|
];
|
||||||
|
fields(upgrade_history) ->
|
||||||
|
[
|
||||||
|
{started_at,
|
||||||
|
hoconsc:mk(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
desc => <<"The timestamp (in format of RFC3339) when the upgrade started">>,
|
||||||
|
example => <<"2024-07-15T13:48:02.648559+08:00">>
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{finished_at,
|
||||||
|
hoconsc:mk(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
desc => <<"The timestamp (in format of RFC3339) when the upgrade finished">>,
|
||||||
|
example => <<"2024-07-16T11:00:01.875627+08:00">>
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{from_vsn,
|
||||||
|
hoconsc:mk(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
desc => <<"The version before the upgrade">>,
|
||||||
|
example => ?EXAM_VSN1
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{target_vsn,
|
||||||
|
hoconsc:mk(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
desc => <<"The target version of the upgrade">>,
|
||||||
|
example => ?EXAM_VSN3
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{upgrade_opts,
|
||||||
|
hoconsc:mk(
|
||||||
|
map(),
|
||||||
|
#{
|
||||||
|
desc => <<"The options used for the upgrade">>,
|
||||||
|
example => #{deploy_inplace => false}
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{status,
|
||||||
|
hoconsc:mk(
|
||||||
|
hoconsc:enum(['in-progress', finished]),
|
||||||
|
#{
|
||||||
|
desc => <<"The upgrade status of the node">>,
|
||||||
|
example => 'in-progress'
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{result,
|
||||||
|
hoconsc:mk(
|
||||||
|
hoconsc:union([success, hoconsc:ref(?MODULE, upgrade_error)]),
|
||||||
|
#{
|
||||||
|
desc => <<"The upgrade result">>,
|
||||||
|
example => success
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
];
|
||||||
|
fields(running_status) ->
|
||||||
|
[
|
||||||
|
{node, hoconsc:mk(binary(), #{example => <<"emqx@127.0.0.1">>})},
|
||||||
|
{status,
|
||||||
|
hoconsc:mk(hoconsc:enum(['in-progress', idle]), #{
|
||||||
|
desc => <<
|
||||||
|
"The upgrade status of a node:<br/>"
|
||||||
|
"1. in-progress: hot upgrade is in progress.<br/>"
|
||||||
|
"2. idle: hot upgrade is not started.<br/>"
|
||||||
|
>>
|
||||||
|
})},
|
||||||
|
{role,
|
||||||
|
hoconsc:mk(hoconsc:enum([core, replicant]), #{
|
||||||
|
desc => <<"The role of the node">>,
|
||||||
|
example => core
|
||||||
|
})},
|
||||||
|
{live_connections,
|
||||||
|
hoconsc:mk(integer(), #{
|
||||||
|
desc => <<"The number of live connections">>,
|
||||||
|
example => 100
|
||||||
|
})},
|
||||||
|
{current_vsn,
|
||||||
|
hoconsc:mk(binary(), #{
|
||||||
|
desc => <<"The current version of the node">>,
|
||||||
|
example => ?EXAM_VSN1
|
||||||
|
})},
|
||||||
|
{upgrade_history,
|
||||||
|
hoconsc:mk(
|
||||||
|
hoconsc:array(hoconsc:ref(upgrade_history)),
|
||||||
|
#{
|
||||||
|
desc => <<"The upgrade history of the node">>,
|
||||||
|
example => [
|
||||||
|
#{
|
||||||
|
started_at => <<"2024-07-15T13:48:02.648559+08:00">>,
|
||||||
|
finished_at => <<"2024-07-16T11:00:01.875627+08:00">>,
|
||||||
|
from_vsn => ?EXAM_VSN1,
|
||||||
|
target_vsn => ?EXAM_VSN2,
|
||||||
|
upgrade_opts => #{deploy_inplace => false},
|
||||||
|
status => finished,
|
||||||
|
result => success
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
];
|
||||||
|
fields(upgrade_error) ->
|
||||||
|
[
|
||||||
|
{err_type,
|
||||||
|
hoconsc:mk(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
desc => <<"The type of the error">>,
|
||||||
|
example => <<"no_write_permission">>
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{details,
|
||||||
|
hoconsc:mk(
|
||||||
|
map(),
|
||||||
|
#{
|
||||||
|
desc => <<"The details of the error">>,
|
||||||
|
example => #{
|
||||||
|
dir => <<"emqx/relup">>,
|
||||||
|
msg => <<"no write permission in dir 'emqx/relup'">>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
];
|
||||||
|
fields(node_name) ->
|
||||||
|
[
|
||||||
|
{node,
|
||||||
|
hoconsc:mk(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
default => all,
|
||||||
|
in => path,
|
||||||
|
desc => <<"The node to be upgraded">>,
|
||||||
|
example => <<"emqx@127.0.0.1">>
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
].
|
||||||
|
|
||||||
|
validate_name(Name) ->
|
||||||
|
NameLen = byte_size(Name),
|
||||||
|
case NameLen > 0 andalso NameLen =< 256 of
|
||||||
|
true ->
|
||||||
|
case re:run(Name, ?NAME_RE) of
|
||||||
|
nomatch -> {error, <<"Name should be " ?NAME_RE>>};
|
||||||
|
_ -> ok
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
{error, <<"Name Length must =< 256">>}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%==============================================================================
|
||||||
|
%% HTTP API CallBacks
|
||||||
|
|
||||||
|
'/relup/package/upload'(post, #{body := #{<<"plugin">> := UploadBody}} = Params) ->
|
||||||
|
case assert_relup_pkg_name(UploadBody) of
|
||||||
|
{ok, NameVsn} ->
|
||||||
|
case get_installed_packages() of
|
||||||
|
[] ->
|
||||||
|
install_as_hidden_plugin(NameVsn, Params);
|
||||||
|
_ ->
|
||||||
|
return_bad_request(
|
||||||
|
<<
|
||||||
|
"Only one relup package can be installed at a time."
|
||||||
|
"Please delete the existing package first."
|
||||||
|
>>
|
||||||
|
)
|
||||||
|
end;
|
||||||
|
{error, Reason} ->
|
||||||
|
return_bad_request(Reason)
|
||||||
|
end.
|
||||||
|
|
||||||
|
'/relup/package'(get, _) ->
|
||||||
|
case get_installed_packages() of
|
||||||
|
[PluginInfo] ->
|
||||||
|
{200, format_package_info(PluginInfo)};
|
||||||
|
[] ->
|
||||||
|
return_not_found(<<"No relup package is installed">>)
|
||||||
|
end;
|
||||||
|
'/relup/package'(delete, _) ->
|
||||||
|
delete_installed_packages(),
|
||||||
|
{204}.
|
||||||
|
|
||||||
|
'/relup/status'(get, _) ->
|
||||||
|
{[_ | _] = Res, []} = emqx_mgmt_api_relup_proto_v1:get_upgrade_status_from_all_nodes(),
|
||||||
|
case
|
||||||
|
lists:filter(
|
||||||
|
fun
|
||||||
|
(R) when is_map(R) -> false;
|
||||||
|
(_) -> true
|
||||||
|
end,
|
||||||
|
Res
|
||||||
|
)
|
||||||
|
of
|
||||||
|
[] ->
|
||||||
|
{200, Res};
|
||||||
|
Filtered ->
|
||||||
|
return_internal_error(
|
||||||
|
case hd(Filtered) of
|
||||||
|
{badrpc, Reason} -> Reason;
|
||||||
|
Reason -> Reason
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end.
|
||||||
|
|
||||||
|
'/relup/status/:node'(get, #{bindings := #{node := NodeNameStr}}) ->
|
||||||
|
emqx_utils_api:with_node(
|
||||||
|
NodeNameStr,
|
||||||
|
fun
|
||||||
|
(Node) when node() =:= Node ->
|
||||||
|
{200, get_upgrade_status()};
|
||||||
|
(Node) when is_atom(Node) ->
|
||||||
|
{200, emqx_mgmt_api_relup_proto_v1:get_upgrade_status(Node)}
|
||||||
|
end
|
||||||
|
).
|
||||||
|
|
||||||
|
'/relup/upgrade'(post, _) ->
|
||||||
|
?ASSERT_PKG_READY(
|
||||||
|
upgrade_with_targe_vsn(fun(TargetVsn) ->
|
||||||
|
run_upgrade_on_nodes(emqx:running_nodes(), TargetVsn)
|
||||||
|
end)
|
||||||
|
).
|
||||||
|
|
||||||
|
'/relup/upgrade/:node'(post, #{bindings := #{node := NodeNameStr}}) ->
|
||||||
|
?ASSERT_PKG_READY(
|
||||||
|
upgrade_with_targe_vsn(
|
||||||
|
fun(TargetVsn) ->
|
||||||
|
emqx_utils_api:with_node(
|
||||||
|
NodeNameStr,
|
||||||
|
fun
|
||||||
|
(Node) when node() =:= Node ->
|
||||||
|
run_upgrade(TargetVsn);
|
||||||
|
(Node) when is_atom(Node) ->
|
||||||
|
run_upgrade_on_nodes([Node], TargetVsn)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
).
|
||||||
|
%%==============================================================================
|
||||||
|
%% Helper functions
|
||||||
|
|
||||||
|
install_as_hidden_plugin(NameVsn, Params) ->
|
||||||
|
case emqx_mgmt_api_plugins:upload_install(post, Params) of
|
||||||
|
{204} ->
|
||||||
|
case emqx_mgmt_api_plugins_proto_v3:ensure_action(NameVsn, start) of
|
||||||
|
ok ->
|
||||||
|
{204};
|
||||||
|
{error, Reason} ->
|
||||||
|
%% try our best to clean up if start failed
|
||||||
|
_ = emqx_mgmt_api_plugins_proto_v3:delete_package(NameVsn),
|
||||||
|
return_internal_error(Reason)
|
||||||
|
end;
|
||||||
|
ErrResp ->
|
||||||
|
ErrResp
|
||||||
|
end.
|
||||||
|
|
||||||
|
assert_relup_pkg_name(UploadBody) ->
|
||||||
|
[{FileName, _Bin}] = maps:to_list(maps:without([type], UploadBody)),
|
||||||
|
case string:split(FileName, "-") of
|
||||||
|
[?PLUGIN_NAME, _] ->
|
||||||
|
{ok, string:trim(FileName, trailing, ".tar.gz")};
|
||||||
|
_ ->
|
||||||
|
{error, <<"Invalid relup package name: ", FileName/binary>>}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_upgrade_status() ->
|
||||||
|
#{
|
||||||
|
node => node(),
|
||||||
|
role => mria_rlog:role(),
|
||||||
|
live_connections => emqx_cm:get_connected_client_count(),
|
||||||
|
current_vsn => list_to_binary(emqx_release:version()),
|
||||||
|
status => call_emqx_relup_main(get_latest_upgrade_status, [], idle),
|
||||||
|
upgrade_history => call_emqx_relup_main(get_all_upgrade_logs, [], [])
|
||||||
|
}.
|
||||||
|
|
||||||
|
call_emqx_relup_main(Fun, Args, Default) ->
|
||||||
|
case erlang:function_exported(emqx_relup_main, Fun, length(Args)) of
|
||||||
|
true ->
|
||||||
|
erlang:apply(emqx_relup_main, Fun, Args);
|
||||||
|
false ->
|
||||||
|
%% relup package is not installed
|
||||||
|
Default
|
||||||
|
end.
|
||||||
|
|
||||||
|
upgrade_with_targe_vsn(Fun) ->
|
||||||
|
case get_target_vsn() of
|
||||||
|
{ok, TargetVsn} ->
|
||||||
|
Fun(TargetVsn);
|
||||||
|
{error, no_relup_package_installed} ->
|
||||||
|
return_package_not_installed();
|
||||||
|
{error, multiple_relup_packages_installed} ->
|
||||||
|
return_internal_error(<<"Multiple relup package installed">>)
|
||||||
|
end.
|
||||||
|
|
||||||
|
run_upgrade_on_nodes(Nodes, TargetVsn) ->
|
||||||
|
{[_ | _] = Res, []} = emqx_mgmt_api_relup_proto_v1:run_upgrade(Nodes, TargetVsn),
|
||||||
|
case lists:filter(fun(R) -> R =/= ok end, Res) of
|
||||||
|
[] ->
|
||||||
|
{204};
|
||||||
|
Filtered ->
|
||||||
|
case hd(Filtered) of
|
||||||
|
no_pkg_installed -> return_package_not_installed();
|
||||||
|
{badrpc, Reason} -> return_internal_error(Reason);
|
||||||
|
{error, Reason} -> upgrade_return(Reason);
|
||||||
|
Reason -> return_internal_error(Reason)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
run_upgrade(TargetVsn) ->
|
||||||
|
case emqx_relup_upgrade(TargetVsn) of
|
||||||
|
no_pkg_installed -> return_package_not_installed();
|
||||||
|
ok -> {204};
|
||||||
|
{error, Reason} -> upgrade_return(Reason)
|
||||||
|
end.
|
||||||
|
|
||||||
|
emqx_relup_upgrade(TargetVsn) ->
|
||||||
|
call_emqx_relup_main(upgrade, [TargetVsn], no_pkg_installed).
|
||||||
|
|
||||||
|
get_target_vsn() ->
|
||||||
|
case get_installed_packages() of
|
||||||
|
[PackageInfo] -> {ok, target_vsn_from_rel_vsn(maps_get(rel_vsn, PackageInfo))};
|
||||||
|
[] -> {error, no_relup_package_installed};
|
||||||
|
_ -> {error, multiple_relup_packages_installed}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_installed_packages() ->
|
||||||
|
lists:filtermap(
|
||||||
|
fun(PackageInfo) ->
|
||||||
|
case maps_get(name, PackageInfo) of
|
||||||
|
?PLUGIN_NAME -> true;
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
emqx_plugins:list(hidden)
|
||||||
|
).
|
||||||
|
|
||||||
|
target_vsn_from_rel_vsn(Vsn) ->
|
||||||
|
case string:split(binary_to_list(Vsn), "-") of
|
||||||
|
[_] -> throw({invalid_vsn, Vsn});
|
||||||
|
[VsnStr | _] -> VsnStr
|
||||||
|
end.
|
||||||
|
|
||||||
|
delete_installed_packages() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun(PackageInfo) ->
|
||||||
|
ok = emqx_mgmt_api_plugins_proto_v3:delete_package(
|
||||||
|
name_vsn(?PLUGIN_NAME, maps_get(rel_vsn, PackageInfo))
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
get_installed_packages()
|
||||||
|
).
|
||||||
|
|
||||||
|
format_package_info(PluginInfo) when is_map(PluginInfo) ->
|
||||||
|
Vsn = maps_get(rel_vsn, PluginInfo),
|
||||||
|
TargetVsn = target_vsn_from_rel_vsn(Vsn),
|
||||||
|
case call_emqx_relup_main(get_package_info, [TargetVsn], no_pkg_installed) of
|
||||||
|
no_pkg_installed ->
|
||||||
|
throw({get_pkg_info_failed, <<"No relup package is installed">>});
|
||||||
|
{error, Reason} ->
|
||||||
|
throw({get_pkg_info_failed, Reason});
|
||||||
|
{ok, #{base_vsns := BaseVsns, change_logs := ChangeLogs}} ->
|
||||||
|
#{
|
||||||
|
name => name_vsn(?PLUGIN_NAME, Vsn),
|
||||||
|
target_vsn => Vsn,
|
||||||
|
built_on_otp_release => maps_get(built_on_otp_release, PluginInfo),
|
||||||
|
applicable_vsns => BaseVsns,
|
||||||
|
build_date => maps_get(git_commit_or_build_date, PluginInfo),
|
||||||
|
change_logs => ChangeLogs,
|
||||||
|
md5_sum => maps_get(md5sum, PluginInfo)
|
||||||
|
}
|
||||||
|
end.
|
||||||
|
|
||||||
|
maps_get(Key, Map) when is_atom(Key) ->
|
||||||
|
maps_get(Key, Map, unknown).
|
||||||
|
|
||||||
|
maps_get(Key, Map, Def) when is_atom(Key) ->
|
||||||
|
case maps:find(Key, Map) of
|
||||||
|
{ok, Value} -> Value;
|
||||||
|
error -> maps:get(atom_to_binary(Key, utf8), Map, Def)
|
||||||
|
end.
|
||||||
|
|
||||||
|
upgrade_return(#{stage := check_and_unpack} = Reason) ->
|
||||||
|
return_bad_request(Reason);
|
||||||
|
upgrade_return(Reason) ->
|
||||||
|
return_internal_error(Reason).
|
||||||
|
|
||||||
|
return_not_found(Reason) ->
|
||||||
|
{404, #{
|
||||||
|
code => 'NOT_FOUND',
|
||||||
|
message => emqx_utils:readable_error_msg(Reason)
|
||||||
|
}}.
|
||||||
|
|
||||||
|
return_package_not_installed() ->
|
||||||
|
return_bad_request(<<"No relup package is installed">>).
|
||||||
|
|
||||||
|
return_bad_request(Reason) ->
|
||||||
|
{400, #{
|
||||||
|
code => 'BAD_REQUEST',
|
||||||
|
message => emqx_utils:readable_error_msg(Reason)
|
||||||
|
}}.
|
||||||
|
|
||||||
|
return_internal_error(Reason) ->
|
||||||
|
{500, #{
|
||||||
|
code => 'INTERNAL_ERROR',
|
||||||
|
message => emqx_utils:readable_error_msg(Reason)
|
||||||
|
}}.
|
||||||
|
|
||||||
|
name_vsn(Name, Vsn) ->
|
||||||
|
iolist_to_binary([Name, "-", Vsn]).
|
|
@ -0,0 +1,45 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2022-2024 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_mgmt_api_relup_proto_v1).
|
||||||
|
|
||||||
|
-behaviour(emqx_bpapi).
|
||||||
|
|
||||||
|
-include_lib("emqx/include/bpapi.hrl").
|
||||||
|
|
||||||
|
-export([
|
||||||
|
introduced_in/0,
|
||||||
|
run_upgrade/2,
|
||||||
|
get_upgrade_status_from_all_nodes/0,
|
||||||
|
get_upgrade_status/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-define(RPC_TIMEOUT_OP, 180_000).
|
||||||
|
-define(RPC_TIMEOUT_INFO, 15_000).
|
||||||
|
|
||||||
|
introduced_in() ->
|
||||||
|
"5.8.0".
|
||||||
|
|
||||||
|
-spec run_upgrade([node()], string()) -> emqx_rpc:multicall_result().
|
||||||
|
run_upgrade(Nodes, TargetVsn) ->
|
||||||
|
rpc:multicall(Nodes, emqx_mgmt_api_relup, emqx_relup_upgrade, [TargetVsn], ?RPC_TIMEOUT_OP).
|
||||||
|
|
||||||
|
-spec get_upgrade_status_from_all_nodes() -> emqx_rpc:multicall_result().
|
||||||
|
get_upgrade_status_from_all_nodes() ->
|
||||||
|
rpc:multicall(emqx_mgmt_api_relup, get_upgrade_status, [], ?RPC_TIMEOUT_INFO).
|
||||||
|
|
||||||
|
-spec get_upgrade_status(node()) -> emqx_rpc:call_result(map()).
|
||||||
|
get_upgrade_status(Node) ->
|
||||||
|
rpc:call(Node, emqx_mgmt_api_relup, get_upgrade_status, [], ?RPC_TIMEOUT_INFO).
|
|
@ -56,7 +56,8 @@
|
||||||
ensure_stopped/0,
|
ensure_stopped/0,
|
||||||
ensure_stopped/1,
|
ensure_stopped/1,
|
||||||
restart/1,
|
restart/1,
|
||||||
list/0
|
list/0,
|
||||||
|
list/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% Plugin config APIs
|
%% Plugin config APIs
|
||||||
|
@ -73,6 +74,7 @@
|
||||||
decode_plugin_config_map/2,
|
decode_plugin_config_map/2,
|
||||||
install_dir/0,
|
install_dir/0,
|
||||||
avsc_file_path/1,
|
avsc_file_path/1,
|
||||||
|
md5sum_file/1,
|
||||||
with_plugin_avsc/1
|
with_plugin_avsc/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
@ -377,13 +379,17 @@ restart(NameVsn) ->
|
||||||
%% Including the ones that are installed, but not enabled in config.
|
%% Including the ones that are installed, but not enabled in config.
|
||||||
-spec list() -> [plugin_info()].
|
-spec list() -> [plugin_info()].
|
||||||
list() ->
|
list() ->
|
||||||
|
list(normal).
|
||||||
|
|
||||||
|
-spec list(all | normal | hidden) -> [plugin_info()].
|
||||||
|
list(Type) ->
|
||||||
Pattern = filename:join([install_dir(), "*", "release.json"]),
|
Pattern = filename:join([install_dir(), "*", "release.json"]),
|
||||||
All = lists:filtermap(
|
All = lists:filtermap(
|
||||||
fun(JsonFilePath) ->
|
fun(JsonFilePath) ->
|
||||||
[_, NameVsn | _] = lists:reverse(filename:split(JsonFilePath)),
|
[_, NameVsn | _] = lists:reverse(filename:split(JsonFilePath)),
|
||||||
case read_plugin_info(NameVsn, #{}) of
|
case read_plugin_info(NameVsn, #{}) of
|
||||||
{ok, Info} ->
|
{ok, Info} ->
|
||||||
{true, Info};
|
filter_plugin_of_type(Type, Info);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?SLOG(warning, Reason#{msg => "failed_to_read_plugin_info"}),
|
?SLOG(warning, Reason#{msg => "failed_to_read_plugin_info"}),
|
||||||
false
|
false
|
||||||
|
@ -393,6 +399,17 @@ list() ->
|
||||||
),
|
),
|
||||||
do_list(configured(), All).
|
do_list(configured(), All).
|
||||||
|
|
||||||
|
filter_plugin_of_type(all, Info) ->
|
||||||
|
{true, Info};
|
||||||
|
filter_plugin_of_type(normal, #{<<"hidden">> := true}) ->
|
||||||
|
false;
|
||||||
|
filter_plugin_of_type(normal, Info) ->
|
||||||
|
{true, Info};
|
||||||
|
filter_plugin_of_type(hidden, #{<<"hidden">> := true} = Info) ->
|
||||||
|
{true, Info};
|
||||||
|
filter_plugin_of_type(hidden, _Info) ->
|
||||||
|
false.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Package utils
|
%% Package utils
|
||||||
|
|
||||||
|
@ -661,10 +678,6 @@ do_list([#{name_vsn := NameVsn} | Rest], All) ->
|
||||||
end,
|
end,
|
||||||
case lists:splitwith(SplitF, All) of
|
case lists:splitwith(SplitF, All) of
|
||||||
{_, []} ->
|
{_, []} ->
|
||||||
?SLOG(warning, #{
|
|
||||||
msg => "configured_plugin_not_installed",
|
|
||||||
name_vsn => NameVsn
|
|
||||||
}),
|
|
||||||
do_list(Rest, All);
|
do_list(Rest, All);
|
||||||
{Front, [I | Rear]} ->
|
{Front, [I | Rear]} ->
|
||||||
[I | do_list(Rest, Front ++ Rear)]
|
[I | do_list(Rest, Front ++ Rear)]
|
||||||
|
@ -736,7 +749,8 @@ do_read_plugin(NameVsn, InfoFilePath, Options) ->
|
||||||
{ok, PlainMap} = (read_file_fun(InfoFilePath, "bad_info_file", #{read_mode => ?JSON_MAP}))(),
|
{ok, PlainMap} = (read_file_fun(InfoFilePath, "bad_info_file", #{read_mode => ?JSON_MAP}))(),
|
||||||
Info0 = check_plugin(PlainMap, NameVsn, InfoFilePath),
|
Info0 = check_plugin(PlainMap, NameVsn, InfoFilePath),
|
||||||
Info1 = plugins_readme(NameVsn, Options, Info0),
|
Info1 = plugins_readme(NameVsn, Options, Info0),
|
||||||
plugin_status(NameVsn, Info1).
|
Info2 = plugins_package_info(NameVsn, Info1),
|
||||||
|
plugin_status(NameVsn, Info2).
|
||||||
|
|
||||||
read_plugin_avsc(NameVsn) ->
|
read_plugin_avsc(NameVsn) ->
|
||||||
read_plugin_avsc(NameVsn, #{read_mode => ?JSON_MAP}).
|
read_plugin_avsc(NameVsn, #{read_mode => ?JSON_MAP}).
|
||||||
|
@ -837,6 +851,12 @@ get_plugin_config_from_any_node([Node | T], NameVsn, Errors) ->
|
||||||
get_plugin_config_from_any_node(T, NameVsn, [{Node, Err} | Errors])
|
get_plugin_config_from_any_node(T, NameVsn, [{Node, Err} | Errors])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
plugins_package_info(NameVsn, Info) ->
|
||||||
|
case file:read_file(md5sum_file(NameVsn)) of
|
||||||
|
{ok, MD5} -> Info#{md5sum => MD5};
|
||||||
|
_ -> Info#{md5sum => <<>>}
|
||||||
|
end.
|
||||||
|
|
||||||
plugins_readme(NameVsn, #{fill_readme := true}, Info) ->
|
plugins_readme(NameVsn, #{fill_readme := true}, Info) ->
|
||||||
case file:read_file(readme_file(NameVsn)) of
|
case file:read_file(readme_file(NameVsn)) of
|
||||||
{ok, Bin} -> Info#{readme => Bin};
|
{ok, Bin} -> Info#{readme => Bin};
|
||||||
|
@ -1489,6 +1509,10 @@ default_plugin_config_file(NameVsn) ->
|
||||||
i18n_file_path(NameVsn) ->
|
i18n_file_path(NameVsn) ->
|
||||||
wrap_to_list(filename:join([plugin_priv_dir(NameVsn), "config_i18n.json"])).
|
wrap_to_list(filename:join([plugin_priv_dir(NameVsn), "config_i18n.json"])).
|
||||||
|
|
||||||
|
-spec md5sum_file(name_vsn()) -> string().
|
||||||
|
md5sum_file(NameVsn) ->
|
||||||
|
plugin_dir(NameVsn) ++ ".tar.gz.md5sum".
|
||||||
|
|
||||||
-spec readme_file(name_vsn()) -> string().
|
-spec readme_file(name_vsn()) -> string().
|
||||||
readme_file(NameVsn) ->
|
readme_file(NameVsn) ->
|
||||||
wrap_to_list(filename:join([plugin_dir(NameVsn), "README.md"])).
|
wrap_to_list(filename:join([plugin_dir(NameVsn), "README.md"])).
|
||||||
|
|
|
@ -73,5 +73,7 @@ handle_result({ok, Result}) ->
|
||||||
?OK(Result);
|
?OK(Result);
|
||||||
handle_result({error, Reason}) ->
|
handle_result({error, Reason}) ->
|
||||||
?BAD_REQUEST(Reason);
|
?BAD_REQUEST(Reason);
|
||||||
|
handle_result({HTTPCode}) when is_integer(HTTPCode) ->
|
||||||
|
{HTTPCode};
|
||||||
handle_result({HTTPCode, Content}) when is_integer(HTTPCode) ->
|
handle_result({HTTPCode, Content}) when is_integer(HTTPCode) ->
|
||||||
{HTTPCode, Content}.
|
{HTTPCode, Content}.
|
||||||
|
|
109
bin/emqx
109
bin/emqx
|
@ -13,53 +13,6 @@ if [ "$DEBUG" -eq 2 ]; then
|
||||||
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# We need to find real directory with emqx files on all platforms
|
|
||||||
# even when bin/emqx is symlinked on several levels
|
|
||||||
# - readlink -f works perfectly, but `-f` flag has completely different meaning in BSD version,
|
|
||||||
# so we can't use it universally.
|
|
||||||
# - `stat -f%R` on MacOS does exactly what `readlink -f` does on Linux, but we can't use it
|
|
||||||
# as a universal solution either because GNU stat has different syntax and this argument is invalid.
|
|
||||||
# Also, version of stat which supports this syntax is only available since MacOS 12
|
|
||||||
if [ "$(uname -s)" == 'Darwin' ]; then
|
|
||||||
product_version="$(sw_vers -productVersion | cut -d '.' -f 1)"
|
|
||||||
if [ "$product_version" -ge 12 ]; then
|
|
||||||
# if homebrew coreutils package is installed, GNU version of stat can take precedence,
|
|
||||||
# so we use absolute path to ensure we are calling MacOS default
|
|
||||||
RUNNER_ROOT_DIR="$(cd "$(dirname "$(/usr/bin/stat -f%R "$0" || echo "$0")")"/..; pwd -P)"
|
|
||||||
else
|
|
||||||
# try our best to resolve link on MacOS <= 11
|
|
||||||
RUNNER_ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
RUNNER_ROOT_DIR="$(cd "$(dirname "$(realpath "$0" || echo "$0")")"/..; pwd -P)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# shellcheck disable=SC1090,SC1091
|
|
||||||
. "$RUNNER_ROOT_DIR"/releases/emqx_vars
|
|
||||||
|
|
||||||
# defined in emqx_vars
|
|
||||||
export RUNNER_ROOT_DIR
|
|
||||||
export EMQX_ETC_DIR
|
|
||||||
export REL_VSN
|
|
||||||
export SCHEMA_MOD
|
|
||||||
export IS_ENTERPRISE
|
|
||||||
|
|
||||||
RUNNER_SCRIPT="$RUNNER_BIN_DIR/$REL_NAME"
|
|
||||||
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
|
|
||||||
REL_DIR="$RUNNER_ROOT_DIR/releases/$REL_VSN"
|
|
||||||
|
|
||||||
WHOAMI=$(whoami 2>/dev/null || id -u)
|
|
||||||
|
|
||||||
# hocon try to read environment variables starting with "EMQX_"
|
|
||||||
export HOCON_ENV_OVERRIDE_PREFIX='EMQX_'
|
|
||||||
|
|
||||||
export ERTS_DIR="$RUNNER_ROOT_DIR/erts-$ERTS_VSN"
|
|
||||||
export BINDIR="$ERTS_DIR/bin"
|
|
||||||
export EMU="beam"
|
|
||||||
export PROGNAME="erl"
|
|
||||||
export ERTS_LIB_DIR="$RUNNER_ROOT_DIR/lib"
|
|
||||||
DYNLIBS_DIR="$RUNNER_ROOT_DIR/dynlibs"
|
|
||||||
|
|
||||||
logerr() {
|
logerr() {
|
||||||
if [ "${TERM:-dumb}" = dumb ]; then
|
if [ "${TERM:-dumb}" = dumb ]; then
|
||||||
echo -e "ERROR: $*" 1>&2
|
echo -e "ERROR: $*" 1>&2
|
||||||
|
@ -89,6 +42,64 @@ die() {
|
||||||
exit "$errno"
|
exit "$errno"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# We need to find real directory with emqx files on all platforms
|
||||||
|
# even when bin/emqx is symlinked on several levels
|
||||||
|
# - readlink -f works perfectly, but `-f` flag has completely different meaning in BSD version,
|
||||||
|
# so we can't use it universally.
|
||||||
|
# - `stat -f%R` on MacOS does exactly what `readlink -f` does on Linux, but we can't use it
|
||||||
|
# as a universal solution either because GNU stat has different syntax and this argument is invalid.
|
||||||
|
# Also, version of stat which supports this syntax is only available since MacOS 12
|
||||||
|
if [ "$(uname -s)" == 'Darwin' ]; then
|
||||||
|
product_version="$(sw_vers -productVersion | cut -d '.' -f 1)"
|
||||||
|
if [ "$product_version" -ge 12 ]; then
|
||||||
|
# if homebrew coreutils package is installed, GNU version of stat can take precedence,
|
||||||
|
# so we use absolute path to ensure we are calling MacOS default
|
||||||
|
RUNNER_ROOT_DIR="$(cd "$(dirname "$(/usr/bin/stat -f%R "$0" || echo "$0")")"/..; pwd -P)"
|
||||||
|
else
|
||||||
|
# try our best to resolve link on MacOS <= 11
|
||||||
|
RUNNER_ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
RUNNER_ROOT_DIR="$(cd "$(dirname "$(realpath "$0" || echo "$0")")"/..; pwd -P)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASE_RUNNER_ROOT_DIR="${BASE_RUNNER_ROOT_DIR:-$RUNNER_ROOT_DIR}"
|
||||||
|
|
||||||
|
if [ -f "$RUNNER_ROOT_DIR/relup/version" ]; then
|
||||||
|
TARGET_VSN=$(cat "$RUNNER_ROOT_DIR/relup/version")
|
||||||
|
export BASE_RUNNER_ROOT_DIR
|
||||||
|
logwarn "Loading emqx from hot upgrade dir: $RUNNER_ROOT_DIR/relup"
|
||||||
|
exec "$RUNNER_ROOT_DIR"/relup/"$TARGET_VSN"/bin/emqx "$@"
|
||||||
|
else
|
||||||
|
logdebug "Loading emqx from $RUNNER_ROOT_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$RUNNER_ROOT_DIR"/releases/emqx_vars
|
||||||
|
|
||||||
|
# defined in emqx_vars
|
||||||
|
export RUNNER_ROOT_DIR
|
||||||
|
export EMQX_ETC_DIR
|
||||||
|
export REL_VSN
|
||||||
|
export SCHEMA_MOD
|
||||||
|
export IS_ENTERPRISE
|
||||||
|
|
||||||
|
RUNNER_SCRIPT="$RUNNER_BIN_DIR/$REL_NAME"
|
||||||
|
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
|
||||||
|
REL_DIR="$RUNNER_ROOT_DIR/releases/$REL_VSN"
|
||||||
|
|
||||||
|
WHOAMI=$(whoami 2>/dev/null || id -u)
|
||||||
|
|
||||||
|
# hocon try to read environment variables starting with "EMQX_"
|
||||||
|
export HOCON_ENV_OVERRIDE_PREFIX='EMQX_'
|
||||||
|
|
||||||
|
export ERTS_DIR="$RUNNER_ROOT_DIR/erts-$ERTS_VSN"
|
||||||
|
export BINDIR="$ERTS_DIR/bin"
|
||||||
|
export EMU="beam"
|
||||||
|
export PROGNAME="erl"
|
||||||
|
export ERTS_LIB_DIR="$RUNNER_ROOT_DIR/lib"
|
||||||
|
DYNLIBS_DIR="$RUNNER_ROOT_DIR/dynlibs"
|
||||||
|
|
||||||
assert_node_alive() {
|
assert_node_alive() {
|
||||||
if ! relx_nodetool "ping" > /dev/null; then
|
if ! relx_nodetool "ping" > /dev/null; then
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -598,7 +609,7 @@ DATA_DIR="$(get_boot_config 'node.data_dir')"
|
||||||
DATA_DIR="${DATA_DIR%/}"
|
DATA_DIR="${DATA_DIR%/}"
|
||||||
if [[ $DATA_DIR != /* ]]; then
|
if [[ $DATA_DIR != /* ]]; then
|
||||||
# relative path
|
# relative path
|
||||||
DATA_DIR="${RUNNER_ROOT_DIR}/${DATA_DIR}"
|
DATA_DIR="${BASE_RUNNER_ROOT_DIR}/${DATA_DIR}"
|
||||||
fi
|
fi
|
||||||
CONFIGS_DIR="$DATA_DIR/configs"
|
CONFIGS_DIR="$DATA_DIR/configs"
|
||||||
mkdir -p "$CONFIGS_DIR"
|
mkdir -p "$CONFIGS_DIR"
|
||||||
|
@ -1060,7 +1071,7 @@ nodetool_shutdown() {
|
||||||
logger -t "${REL_NAME}[${PID}]" "STOP: OK"
|
logger -t "${REL_NAME}[${PID}]" "STOP: OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
cd "$RUNNER_ROOT_DIR"
|
cd "$BASE_RUNNER_ROOT_DIR"
|
||||||
|
|
||||||
case "${COMMAND}" in
|
case "${COMMAND}" in
|
||||||
start)
|
start)
|
||||||
|
|
|
@ -4,6 +4,7 @@ set -eu
|
||||||
# shellcheck disable=SC1090,SC1091
|
# shellcheck disable=SC1090,SC1091
|
||||||
RUNNER_ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)"
|
RUNNER_ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)"
|
||||||
echo "Running node dump in ${RUNNER_ROOT_DIR}"
|
echo "Running node dump in ${RUNNER_ROOT_DIR}"
|
||||||
|
BASE_RUNNER_ROOT_DIR="${BASE_RUNNER_ROOT_DIR:-$RUNNER_ROOT_DIR}"
|
||||||
|
|
||||||
# shellcheck disable=SC1090,SC1091
|
# shellcheck disable=SC1090,SC1091
|
||||||
. "$RUNNER_ROOT_DIR"/releases/emqx_vars
|
. "$RUNNER_ROOT_DIR"/releases/emqx_vars
|
||||||
|
|
36
build
36
build
|
@ -211,39 +211,11 @@ make_elixir_rel() {
|
||||||
assert_no_excluded_deps emqx-enterprise emqx_telemetry
|
assert_no_excluded_deps emqx-enterprise emqx_telemetry
|
||||||
}
|
}
|
||||||
|
|
||||||
## extract previous version .tar.gz files to _build/$PROFILE/rel/emqx before making relup
|
|
||||||
make_relup() {
|
make_relup() {
|
||||||
local rel_dir="_build/$PROFILE/rel/emqx"
|
RELUP_TARGET_VSN="$(./pkg-vsn.sh "$PROFILE" --long)"
|
||||||
local name_pattern
|
export RELUP_TARGET_VSN
|
||||||
name_pattern="${PROFILE}-$(./pkg-vsn.sh "$PROFILE" --vsn_matcher --long)"
|
./rebar3 emqx relup_gen --relup-dir=./rel/relup
|
||||||
local releases=()
|
make rel -C _build/default/plugins/emqx_relup
|
||||||
mkdir -p _upgrade_base
|
|
||||||
while read -r tgzfile ; do
|
|
||||||
local base_vsn
|
|
||||||
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"
|
|
||||||
mkdir -p "${rel_dir}/releases/"
|
|
||||||
cp -npr "$tmp_dir/releases"/* "${rel_dir}/releases/"
|
|
||||||
## There is for some reason a copy of the '$PROFILE.rel' file to releases dir,
|
|
||||||
## the content is duplicated to releases/5.0.0/$PROFILE.rel.
|
|
||||||
## This file seems to be useless, but yet confusing as it does not change after upgrade/downgrade
|
|
||||||
## Hence we force delete this file.
|
|
||||||
rm -f "${rel_dir}/releases/${PROFILE}.rel"
|
|
||||||
mkdir -p "${rel_dir}/lib/"
|
|
||||||
cp -npr "$tmp_dir/lib"/* "${rel_dir}/lib/"
|
|
||||||
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
|
|
||||||
log "No upgrade base found, relup ignored"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
RELX_BASE_VERSIONS="$(IFS=, ; echo "${releases[*]}")"
|
|
||||||
export RELX_BASE_VERSIONS
|
|
||||||
./rebar3 as "$PROFILE" relup --relname emqx --relvsn "${PKG_VSN}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cp_dyn_libs() {
|
cp_dyn_libs() {
|
||||||
|
|
|
@ -128,7 +128,10 @@
|
||||||
% generated code for protobuf
|
% generated code for protobuf
|
||||||
emqx_exhook_pb,
|
emqx_exhook_pb,
|
||||||
% generated code for protobuf
|
% generated code for protobuf
|
||||||
emqx_exproto_pb
|
emqx_exproto_pb,
|
||||||
|
% maybe BUILD_WITHOUT_QUIC
|
||||||
|
emqx_quic_connection,
|
||||||
|
quicer_listener
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{eunit_opts, [verbose, {print_depth, 100}]}.
|
{eunit_opts, [verbose, {print_depth, 100}]}.
|
||||||
|
|
|
@ -185,7 +185,7 @@ project_app_excluded("apps/" ++ AppStr, ExcludedApps) ->
|
||||||
|
|
||||||
plugins() ->
|
plugins() ->
|
||||||
[
|
[
|
||||||
%{relup_helper, {git, "https://github.com/emqx/relup_helper", {tag, "2.1.0"}}},
|
{emqx_relup, {git, "https://github.com/emqx/emqx-relup.git", {tag, "0.1.1"}}},
|
||||||
%% emqx main project does not require port-compiler
|
%% emqx main project does not require port-compiler
|
||||||
%% pin at root level for deterministic
|
%% pin at root level for deterministic
|
||||||
{pc, "v1.14.0"}
|
{pc, "v1.14.0"}
|
||||||
|
@ -393,9 +393,9 @@ overlay_vars_pkg(bin) ->
|
||||||
{platform_etc_dir, "etc"},
|
{platform_etc_dir, "etc"},
|
||||||
{platform_plugins_dir, "plugins"},
|
{platform_plugins_dir, "plugins"},
|
||||||
{runner_bin_dir, "$RUNNER_ROOT_DIR/bin"},
|
{runner_bin_dir, "$RUNNER_ROOT_DIR/bin"},
|
||||||
{emqx_etc_dir, "$RUNNER_ROOT_DIR/etc"},
|
{emqx_etc_dir, "$BASE_RUNNER_ROOT_DIR/etc"},
|
||||||
{runner_lib_dir, "$RUNNER_ROOT_DIR/lib"},
|
{runner_lib_dir, "$RUNNER_ROOT_DIR/lib"},
|
||||||
{runner_log_dir, "$RUNNER_ROOT_DIR/log"},
|
{runner_log_dir, "$BASE_RUNNER_ROOT_DIR/log"},
|
||||||
{runner_user, ""},
|
{runner_user, ""},
|
||||||
{is_elixir, "no"}
|
{is_elixir, "no"}
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#{
|
||||||
|
target_version => "5.6.1",
|
||||||
|
from_version => "5.6.0",
|
||||||
|
code_changes =>
|
||||||
|
[ {load_module, emqx_broker}
|
||||||
|
],
|
||||||
|
post_upgrade_callbacks => []
|
||||||
|
}.
|
|
@ -0,0 +1,44 @@
|
||||||
|
%% This file contains instructions for upgrading from the LAST version to
|
||||||
|
%% the CURRENT version.
|
||||||
|
%%
|
||||||
|
%% We only need to write instructions for the changes in the specific pull request,
|
||||||
|
%% these files will be automatically merged/aggregated into a complete relup file
|
||||||
|
%% before a new version is released.
|
||||||
|
%%
|
||||||
|
%% Note that we do not support the 'apply' command in the 'code_changes' section.
|
||||||
|
%% If any complex operations are needed during the upgrade process, please add
|
||||||
|
%% them in the 'post_upgrade_callbacks' section, and implement them in the
|
||||||
|
%% 'emqx_post_upgrade' module. We consolidate the hot upgrade callback code into
|
||||||
|
%% a single module ('emqx_post_upgrade') is to avoid mixing the hot upgrade-related
|
||||||
|
%% code with the main logic code.
|
||||||
|
|
||||||
|
ChildSpec = fun(Mod) ->
|
||||||
|
#{
|
||||||
|
id => Mod,
|
||||||
|
start => {Mod, start_link, []},
|
||||||
|
restart => permanent,
|
||||||
|
shutdown => 5000,
|
||||||
|
type => worker,
|
||||||
|
modules => [Mod]
|
||||||
|
}
|
||||||
|
end.
|
||||||
|
|
||||||
|
#{
|
||||||
|
target_version => "5.6.1+patch.A",
|
||||||
|
from_version => "5.6.1",
|
||||||
|
code_changes =>
|
||||||
|
%% the 'emqx_release' and 'emqx_post_upgrade' will be automatically added,
|
||||||
|
%% no need to include them here
|
||||||
|
[ {load_module, emqx_broker}
|
||||||
|
, {load_module, emqx_metrics}
|
||||||
|
, {load_module, emqx_ds_replication_shard_allocator}
|
||||||
|
, {update, emqx_ds_replication_layer_egress, {advanced, #{}}}
|
||||||
|
],
|
||||||
|
post_upgrade_callbacks =>
|
||||||
|
[
|
||||||
|
%% emqx_post_upgrade:pr12765_update_stats_timer/1
|
||||||
|
pr12765_update_stats_timer,
|
||||||
|
%% emqx_post_upgrade:pr20000_ensure_sup_started/3
|
||||||
|
{pr20000_ensure_sup_started, ["5.6.1+patch.A", ChildSpec(some_mod)]}
|
||||||
|
]
|
||||||
|
}.
|
|
@ -0,0 +1,3 @@
|
||||||
|
[ "5.6.1+patch.A <- 5.6.1 <- 5.6.0"
|
||||||
|
, "5.7.0 <- 5.6.1 <- 5.6.0"
|
||||||
|
].
|
Loading…
Reference in New Issue