diff --git a/.github/workflows/apps_version_check.yaml b/.github/workflows/apps_version_check.yaml index 4975e5c11..a1bf859ba 100644 --- a/.github/workflows/apps_version_check.yaml +++ b/.github/workflows/apps_version_check.yaml @@ -6,7 +6,22 @@ jobs: check_apps_version: runs-on: ubuntu-20.04 + strategy: + matrix: + erl_otp: + - 24.1.5-3 + os: + - ubuntu20.04 + + container: ghcr.io/emqx/emqx-builder/4.4-5:${{ matrix.erl_otp }}-${{ matrix.os }} + steps: - uses: actions/checkout@v1 - name: Check apps version run: ./scripts/apps-version-check.sh + - name: Check relup (ce) + if: endsWith(github.repository, 'emqx') + run: ./scripts/update-appup.sh emqx --check + - name: Check relup (ee) + if: endsWith(github.repository, 'enterprise') + run: ./scripts/update-appup.sh emqx-ee --check diff --git a/CHANGES-4.3.md b/CHANGES-4.3.md index 648086ec4..7f6d2b5a3 100644 --- a/CHANGES-4.3.md +++ b/CHANGES-4.3.md @@ -36,6 +36,7 @@ File format: * Fix Stomp client can not trigger `$event/client_connection` message [#7096] * Fix system memory false alarm at boot * Fix the MQTT-SN message replay when the topic is not registered to the client [#6970] +* Fix rpc get node info maybe crash when other nodes is not ready. ## v4.3.12 ### Important changes diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src index d782f0272..d8968c05f 100644 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src @@ -1,6 +1,6 @@ {application, emqx_auth_mnesia, [{description, "EMQ X Authentication with Mnesia"}, - {vsn, "4.3.5"}, % strict semver, bump manually + {vsn, "4.3.6"}, % strict semver, bump manually {modules, []}, {registered, []}, {applications, [kernel,stdlib,mnesia]}, diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.appup.src b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.appup.src index acaf9403d..46b3c8312 100644 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.appup.src +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.appup.src @@ -13,10 +13,14 @@ {load_module,emqx_acl_mnesia_api,brutal_purge,soft_purge,[]}, {load_module,emqx_acl_mnesia_cli,brutal_purge,soft_purge,[]}]}, {<<"4.3.4">>, - [{load_module,emqx_auth_mnesia,brutal_purge,soft_purge,[]}, + [{load_module,emqx_auth_mnesia_api,brutal_purge,soft_purge,[]}, + {load_module,emqx_auth_mnesia,brutal_purge,soft_purge,[]}, {load_module,emqx_auth_mnesia_cli,brutal_purge,soft_purge,[]}, {load_module,emqx_acl_mnesia,brutal_purge,soft_purge,[]}, {load_module,emqx_auth_mnesia_app,brutal_purge,soft_purge,[]}]}, + {<<"4.3.5">>, + [{load_module,emqx_auth_mnesia_api,brutal_purge,soft_purge,[]}, + {load_module,emqx_auth_mnesia,brutal_purge,soft_purge,[]}]}, {<<".*">>,[]}], [{<<"4.3.[0-3]">>, [{load_module,emqx_auth_mnesia_cli,brutal_purge,soft_purge,[]}, @@ -31,8 +35,12 @@ {delete_module,emqx_acl_mnesia_migrator}, {delete_module,emqx_acl_mnesia_db}]}, {<<"4.3.4">>, - [{load_module,emqx_auth_mnesia,brutal_purge,soft_purge,[]}, + [{load_module,emqx_auth_mnesia_api,brutal_purge,soft_purge,[]}, + {load_module,emqx_auth_mnesia,brutal_purge,soft_purge,[]}, {load_module,emqx_auth_mnesia_cli,brutal_purge,soft_purge,[]}, {load_module,emqx_acl_mnesia,brutal_purge,soft_purge,[]}, {load_module,emqx_auth_mnesia_app,brutal_purge,soft_purge,[]}]}, + {<<"4.3.5">>, + [{load_module,emqx_auth_mnesia_api,brutal_purge,soft_purge,[]}, + {load_module,emqx_auth_mnesia,brutal_purge,soft_purge,[]}]}, {<<".*">>,[]}]}. diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl index 72b9cfd2b..386adba4b 100644 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% Copyright (c) 2020-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. @@ -72,7 +72,8 @@ check(ClientInfo = #{ clientid := Clientid List -> case match_password(NPassword, HashType, List) of false -> - ?LOG(error, "[Mnesia] Auth from mnesia failed: ~p", [ClientInfo]), + Info = maps:without([password], ClientInfo), + ?LOG(info, "[Mnesia] Auth from mnesia failed: ~p", [Info]), emqx_metrics:inc(?AUTH_METRICS(failure)), {stop, AuthResult#{anonymous => false, auth_result => password_error}}; _ -> diff --git a/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl b/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl index 1878dec45..94d354e25 100644 --- a/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl +++ b/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl @@ -47,6 +47,7 @@ groups() -> ]}]. init_per_suite(Config) -> + application:load(emqx_plugin_libs), emqx_ct_helpers:start_apps( [emqx_modules, emqx_management, emqx_auth_mnesia] , fun set_special_configs/1 ), diff --git a/apps/emqx_rule_engine/src/emqx_rule_engine.app.src b/apps/emqx_rule_engine/src/emqx_rule_engine.app.src index eaeded042..bcf2ee469 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_engine.app.src +++ b/apps/emqx_rule_engine/src/emqx_rule_engine.app.src @@ -1,6 +1,6 @@ {application, emqx_rule_engine, [{description, "EMQ X Rule Engine"}, - {vsn, "4.4.1"}, % strict semver, bump manually! + {vsn, "4.4.2"}, % strict semver, bump manually! {modules, []}, {registered, [emqx_rule_engine_sup, emqx_rule_registry]}, {applications, [kernel,stdlib,rulesql,getopt]}, diff --git a/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src b/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src index 9c8abd5ed..b4abcd68c 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src +++ b/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src @@ -1,15 +1,23 @@ %% -*- mode: erlang -*- {VSN, - [{"4.4.0", - [ {update, emqx_rule_metrics, {advanced, ["4.4.0"]}} + [{"4.4.1", + [ {load_module,emqx_rule_registry,brutal_purge,soft_purge,[]} + ]}, + {"4.4.0", + [ {load_module,emqx_rule_registry,brutal_purge,soft_purge,[]} + , {update, emqx_rule_metrics, {advanced, ["4.4.0"]}} , {load_module,emqx_rule_events,brutal_purge,soft_purge,[]} , {load_module,emqx_rule_engine,brutal_purge,soft_purge,[]} , {load_module,emqx_rule_runtime,brutal_purge,soft_purge,[]} , {load_module,emqx_rule_engine_api,brutal_purge,soft_purge,[]} ]}, {<<".*">>,[]}], - [{"4.4.0", - [ {update, emqx_rule_metrics, {advanced, ["4.4.0"]}} + [{"4.4.1", + [ {load_module,emqx_rule_registry,brutal_purge,soft_purge,[]} + ]}, + {"4.4.0", + [ {load_module,emqx_rule_registry,brutal_purge,soft_purge,[]} + , {update, emqx_rule_metrics, {advanced, ["4.4.0"]}} , {load_module,emqx_rule_events,brutal_purge,soft_purge,[]} , {load_module,emqx_rule_engine,brutal_purge,soft_purge,[]} , {load_module,emqx_rule_runtime,brutal_purge,soft_purge,[]} diff --git a/apps/emqx_sn/rebar.config b/apps/emqx_sn/rebar.config index 5fecbb815..120d4767d 100644 --- a/apps/emqx_sn/rebar.config +++ b/apps/emqx_sn/rebar.config @@ -2,7 +2,7 @@ {plugins, [rebar3_proper]}. {deps, - [{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.4"}}}, + [{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.8.5"}}}, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}} ]}. diff --git a/bin/install_upgrade.escript b/bin/install_upgrade.escript index 0bd4bdee1..ae89ea724 100755 --- a/bin/install_upgrade.escript +++ b/bin/install_upgrade.escript @@ -144,9 +144,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 @@ -161,10 +162,39 @@ 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. + +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 in the following order: @@ -391,3 +421,10 @@ erts_vsn() -> {ok, Str} = file:read_file(filename:join(["releases", "start_erl.data"])), [ErtsVsn, _] = string:tokens(binary_to_list(Str), " "), ErtsVsn. + +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/deploy/charts/emqx/templates/configmap.env.yaml b/deploy/charts/emqx/templates/configmap.env.yaml index 04a72033c..1167b7972 100644 --- a/deploy/charts/emqx/templates/configmap.env.yaml +++ b/deploy/charts/emqx/templates/configmap.env.yaml @@ -11,7 +11,7 @@ metadata: app.kubernetes.io/managed-by: {{ .Release.Service }} data: {{- range $index, $value := .Values.emqxConfig }} - {{- if ne $value nil }} + {{- if $value }} {{- $key := (regexReplaceAllLiteral "\\." (regexReplaceAllLiteral "EMQX[_\\.]" (upper (trimAll " " $index)) "") "__") }} {{ print "EMQX_" $key }}: "{{ tpl (printf "%v" $value) $ }}" {{- end }} diff --git a/rebar.config b/rebar.config index 0778d3961..cd597faed 100644 --- a/rebar.config +++ b/rebar.config @@ -42,7 +42,7 @@ , {gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.8.0"}}} , {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}} , {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.9.0"}}} - , {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.8.4"}}} + , {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.8.5"}}} , {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.8.1.8"}}} , {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.7.0"}}} , {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.3.6"}}} diff --git a/scripts/update-appup.sh b/scripts/update-appup.sh new file mode 100755 index 000000000..5fd26355e --- /dev/null +++ b/scripts/update-appup.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +## This script wrapps update_appup.escript, +## it provides a more commonly used set of default args. + +## Arg1: EMQX PROFILE + +set -euo pipefail + +usage() { + echo "$0 PROFILE PREV_VERSION" +} +# ensure dir +cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")/.." + +PROFILE="${1:-}" +case "$PROFILE" in + emqx-ee) + DIR='enterprise' + TAG_PREFIX='e' + ;; + emqx) + DIR='broker' + TAG_PREFIX='v' + ;; + emqx-edge) + DIR='edge' + TAG_PREFIX='v' + ;; + *) + echo "Unknown profile $PROFILE" + usage + exit 1 + ;; +esac + +PREV_VERSION="$(git describe --tag --match "${TAG_PREFIX}*" | grep -oE "${TAG_PREFIX}4\.[0-9]+\.[0-9]+")" +PREV_VERSION="${PREV_VERSION#[e|v]}" + +shift 1 +ESCRIPT_ARGS="$*" + +OTP_VSN="${OTP_VSN:-$(./scripts/get-otp-vsn.sh)}" + +SYSTEM="${SYSTEM:-$(./scripts/get-distro.sh)}" +if [ -z "${ARCH:-}" ]; then + UNAME="$(uname -m)" + case "$UNAME" in + x86_64) + ARCH='amd64' + ;; + aarch64) + ARCH='arm64' + ;; + arm*) + ARCH='arm' + ;; + esac +fi + +PACKAGE_NAME="${PROFILE}-${PREV_VERSION}-otp${OTP_VSN}-${SYSTEM}-${ARCH}.zip" +DOWNLOAD_URL="https://www.emqx.com/downloads/${DIR}/v${PREV_VERSION}/${PACKAGE_NAME}" + +# shellcheck disable=SC2086 +./scripts/update_appup.escript --make-command "make ${PROFILE}-rel" --binary-rel-url "$DOWNLOAD_URL" $ESCRIPT_ARGS "$PREV_VERSION" diff --git a/scripts/update_appup.escript b/scripts/update_appup.escript index d3d5fd11c..2bb4705b3 100755 --- a/scripts/update_appup.escript +++ b/scripts/update_appup.escript @@ -171,9 +171,11 @@ download_prev_release(Tag, #{binary_rel_url := {ok, URL0}, clone_url := Repo}) - BaseDir = "/tmp/emqx-baseline-bin/", Dir = filename:basename(Repo, ".git") ++ [$-|Tag], Filename = filename:join(BaseDir, Dir), - Script = "echo \"Download: ${OUTFILE}\" && - mkdir -p ${OUTFILE} && - curl -f -L -o ${OUTFILE}.zip ${URL} && + Script = "mkdir -p ${OUTFILE} && + if [ ! -f \"${OUTFILE}.zip\" ]; then \ + echo \"Download: ${OUTFILE}\" && \ + curl -f -L -o \"${OUTFILE}.zip\" \"${URL}\"; \ + fi && unzip -q -n -d ${OUTFILE} ${OUTFILE}.zip", Env = [{"TAG", Tag}, {"OUTFILE", Filename}, {"URL", URL}], bash(Script, Env), @@ -540,20 +542,31 @@ diff_app(UpOrDown, App, , NewModules ), Deleted = maps:keys(maps:without(maps:keys(NewModules), OldModules)), - NChanges = length(New) + length(Changed) + length(Deleted), + Changes = lists:filter(fun({_T, L}) -> length(L) > 0 end, + [{added, New}, {changed, Changed}, {deleted, Deleted}]), case NewVersion =:= OldVersion of - true when NChanges =:= 0 -> + true when Changes =:= [] -> %% no change ok; true -> set_invalid(), - log("ERROR: Application '~p' contains changes, but its version is not updated~n", [App]); + case UpOrDown =:= up of + true -> + %% only log for the upgrade case because it would be the same result + log("ERROR: Application '~p' contains changes, but its version is not updated. ~s", + [App, format_changes(Changes)]); + false -> + ok + end; false -> log("INFO: Application '~p' has been updated: ~p --[~p]--> ~p~n", [App, OldVersion, UpOrDown, NewVersion]), ok end, {New, Changed, Deleted}. +format_changes(Changes) -> + lists:map(fun({Tag, List}) -> io_lib:format("~p: ~p~n", [Tag, List]) end, Changes). + -spec hashsums(file:filename()) -> #{module() => binary()}. hashsums(EbinDir) -> maps:from_list(lists:map( @@ -614,7 +627,9 @@ locate(src, App, Suffix) -> [File] -> {ok, File}; [] -> - undefined + undefined; + Files -> + error({more_than_one_app_found, Files}) end. find_app(Pattern) -> diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 53b4a9ff7..dad0ca480 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -7,6 +7,7 @@ {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_sys,brutal_purge,soft_purge,[]}, {load_module,emqx_connection,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, {load_module,emqx_ctl,brutal_purge,soft_purge,[]}, @@ -16,6 +17,7 @@ {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_sys,brutal_purge,soft_purge,[]}, {load_module,emqx_vm_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, {load_module,emqx_ctl,brutal_purge,soft_purge,[]}, @@ -40,6 +42,7 @@ {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_sys,brutal_purge,soft_purge,[]}, {load_module,emqx_connection,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, {load_module,emqx_ctl,brutal_purge,soft_purge,[]}, @@ -49,6 +52,7 @@ {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_sys,brutal_purge,soft_purge,[]}, {load_module,emqx_vm_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, {load_module,emqx_ctl,brutal_purge,soft_purge,[]}, diff --git a/src/emqx_sys.erl b/src/emqx_sys.erl index 2d816569d..c61272e64 100644 --- a/src/emqx_sys.erl +++ b/src/emqx_sys.erl @@ -94,7 +94,15 @@ sysdescr() -> emqx_app:get_description(). %% @doc Get sys uptime -spec(uptime() -> string()). uptime() -> - gen_server:call(?SYS, uptime). + {TotalWallClock, _} = erlang:statistics(wall_clock), + uptime(TotalWallClock div 1000). + +uptime(Seconds) -> + {D, {H, M, S}} = calendar:seconds_to_daystime(Seconds), + L0 = [{D, " days"}, {H, " hours"}, {M, " minutes"}, {S, " seconds"}], + L1 = lists:dropwhile(fun({K, _}) -> K =:= 0 end, L0), + L2 = lists:map(fun({Time, Unit}) -> [integer_to_list(Time), Unit] end, L1), + lists:flatten(lists:join(", ", L2)). %% @doc Get sys datetime -spec(datetime() -> string()). @@ -137,9 +145,6 @@ heartbeat(State) -> tick(State) -> State#state{ticker = start_timer(sys_interval(), tick)}. -handle_call(uptime, _From, State) -> - {reply, uptime(State), State}; - handle_call(Req, _From, State) -> ?LOG(error, "Unexpected call: ~p", [Req]), {reply, ignored, State}. @@ -149,7 +154,7 @@ handle_cast(Msg, State) -> {noreply, State}. handle_info({timeout, TRef, heartbeat}, State = #state{heartbeat = TRef}) -> - publish_any(uptime, iolist_to_binary(uptime(State))), + publish_any(uptime, iolist_to_binary(uptime())), publish_any(datetime, iolist_to_binary(datetime())), {noreply, heartbeat(State)}; @@ -173,24 +178,6 @@ terminate(_Reason, #state{heartbeat = TRef1, ticker = TRef2}) -> %% Internal functions %%----------------------------------------------------------------------------- -uptime(#state{start_time = Ts}) -> - Secs = timer:now_diff(erlang:timestamp(), Ts) div 1000000, - lists:flatten(uptime(seconds, Secs)). -uptime(seconds, Secs) when Secs < 60 -> - [integer_to_list(Secs), " seconds"]; -uptime(seconds, Secs) -> - [uptime(minutes, Secs div 60), integer_to_list(Secs rem 60), " seconds"]; -uptime(minutes, M) when M < 60 -> - [integer_to_list(M), " minutes, "]; -uptime(minutes, M) -> - [uptime(hours, M div 60), integer_to_list(M rem 60), " minutes, "]; -uptime(hours, H) when H < 24 -> - [integer_to_list(H), " hours, "]; -uptime(hours, H) -> - [uptime(days, H div 24), integer_to_list(H rem 24), " hours, "]; -uptime(days, D) -> - [integer_to_list(D), " days, "]. - publish_any(Name, Value) -> _ = publish(Name, Value), ok. diff --git a/test/emqx_sys_SUITE.erl b/test/emqx_sys_SUITE.erl index 29583524f..e7289cb38 100644 --- a/test/emqx_sys_SUITE.erl +++ b/test/emqx_sys_SUITE.erl @@ -34,7 +34,7 @@ end_per_suite(_Config) -> application:unload(emqx), ok = emqx_logger:set_log_level(error), ok. - + % t_version(_) -> % error('TODO'). @@ -42,10 +42,23 @@ end_per_suite(_Config) -> % error('TODO'). t_uptime(_) -> - ?assertEqual(<<"1 seconds">>, iolist_to_binary(emqx_sys:uptime(seconds, 1))), - ?assertEqual(<<"1 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(seconds, 60))), - ?assertEqual(<<"1 hours, 0 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(seconds, 3600))), - ?assertEqual(<<"1 days, 0 hours, 0 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(seconds, 86400))). + ?assert(is_list(emqx_sys:uptime())), + ?assertEqual(<<"1 seconds">>, iolist_to_binary(emqx_sys:uptime(1))), + ?assertEqual(<<"1 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(60))), + ?assertEqual(<<"1 hours, 0 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(3600))), + ?assertEqual(<<"1 hours, 1 minutes, 1 seconds">>, iolist_to_binary(emqx_sys:uptime(3661))), + ?assertEqual(<<"1 days, 0 hours, 0 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(86400))), + lists:map(fun({D, H, M, S}) -> + Expect = << + (integer_to_binary(D))/binary, " days, ", + (integer_to_binary(H))/binary, " hours, ", + (integer_to_binary(M))/binary, " minutes, ", + (integer_to_binary(S))/binary, " seconds" + >>, + Actual = iolist_to_binary(emqx_sys:uptime(D * 86400 + H * 3600 + M * 60 + S)), + ?assertEqual(Expect, Actual) + end, + [{1, 2, 3, 4}, {10, 20, 30, 40}, {2222, 3, 56, 59}, {59, 23, 59, 59}]). % t_datetime(_) -> % error('TODO').