style(emqx_license): erlfmt refomrat apps/emqx_license
This commit is contained in:
parent
2e05e45245
commit
4f396cceb8
|
@ -1 +1,3 @@
|
|||
{deps, [{emqx, {path, "../../apps/emqx"}}]}.
|
||||
|
||||
{project_plugins, [erlfmt]}.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{application,emqx_license,
|
||||
[{description,"EMQX License"},
|
||||
{application, emqx_license, [
|
||||
{description, "EMQX License"},
|
||||
{vsn, "5.0.0"},
|
||||
{modules, []},
|
||||
{registered, [emqx_license_sup]},
|
||||
{applications, [kernel, stdlib]},
|
||||
{mod,{emqx_license_app,[]}}]}.
|
||||
{mod, {emqx_license_app, []}}
|
||||
]}.
|
||||
|
|
|
@ -10,17 +10,20 @@
|
|||
|
||||
-behaviour(emqx_config_handler).
|
||||
|
||||
-export([pre_config_update/3,
|
||||
-export([
|
||||
pre_config_update/3,
|
||||
post_config_update/5
|
||||
]).
|
||||
|
||||
-export([load/0,
|
||||
-export([
|
||||
load/0,
|
||||
check/2,
|
||||
unload/0,
|
||||
read_license/0,
|
||||
read_license/1,
|
||||
update_file/1,
|
||||
update_key/1]).
|
||||
update_key/1
|
||||
]).
|
||||
|
||||
-define(CONF_KEY_PATH, [license]).
|
||||
|
||||
|
@ -52,7 +55,8 @@ update_file(Filename) when is_binary(Filename); is_list(Filename) ->
|
|||
Result = emqx_conf:update(
|
||||
?CONF_KEY_PATH,
|
||||
{file, Filename},
|
||||
#{rawconf_with_defaults => true, override_to => local}),
|
||||
#{rawconf_with_defaults => true, override_to => local}
|
||||
),
|
||||
handle_config_update_result(Result).
|
||||
|
||||
-spec update_key(binary() | string()) ->
|
||||
|
@ -61,7 +65,8 @@ update_key(Value) when is_binary(Value); is_list(Value) ->
|
|||
Result = emqx_conf:update(
|
||||
?CONF_KEY_PATH,
|
||||
{key, Value},
|
||||
#{rawconf_with_defaults => true, override_to => cluster}),
|
||||
#{rawconf_with_defaults => true, override_to => cluster}
|
||||
),
|
||||
handle_config_update_result(Result).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
|
@ -82,8 +87,10 @@ check(_ConnInfo, AckProps) ->
|
|||
{ok, AckProps}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?SLOG(error, #{msg => "connection_rejected_due_to_license_not_loaded",
|
||||
reason => Reason}),
|
||||
?SLOG(error, #{
|
||||
msg => "connection_rejected_due_to_license_not_loaded",
|
||||
reason => Reason
|
||||
}),
|
||||
{stop, {error, ?RC_QUOTA_EXCEEDED}}
|
||||
end.
|
||||
|
||||
|
@ -98,7 +105,8 @@ post_config_update(_Path, _Cmd, NewConf, _Old, _AppEnvs) ->
|
|||
case read_license(NewConf) of
|
||||
{ok, License} ->
|
||||
{ok, emqx_license_checker:update(License)};
|
||||
{error, _} = Error -> Error
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
|
@ -124,7 +132,6 @@ do_update({file, Filename}, Conf) ->
|
|||
{error, Reason} ->
|
||||
erlang:throw({invalid_license_file, Reason})
|
||||
end;
|
||||
|
||||
do_update({key, Content}, Conf) when is_binary(Content); is_list(Content) ->
|
||||
case emqx_license_parser:parse(Content) of
|
||||
{ok, _License} ->
|
||||
|
@ -144,9 +151,10 @@ read_license(#{file := Filename}) ->
|
|||
{ok, Content} -> emqx_license_parser:parse(Content);
|
||||
{error, _} = Error -> Error
|
||||
end;
|
||||
|
||||
read_license(#{key := Content}) ->
|
||||
emqx_license_parser:parse(Content).
|
||||
|
||||
handle_config_update_result({error, _} = Error) -> Error;
|
||||
handle_config_update_result({ok, #{post_config_update := #{emqx_license := Result}}}) -> {ok, Result}.
|
||||
handle_config_update_result({error, _} = Error) ->
|
||||
Error;
|
||||
handle_config_update_result({ok, #{post_config_update := #{emqx_license := Result}}}) ->
|
||||
{ok, Result}.
|
||||
|
|
|
@ -12,18 +12,22 @@
|
|||
-define(CHECK_INTERVAL, 5000).
|
||||
-define(EXPIRY_ALARM_CHECK_INTERVAL, 24 * 60 * 60).
|
||||
|
||||
-export([start_link/1,
|
||||
-export([
|
||||
start_link/1,
|
||||
start_link/2,
|
||||
update/1,
|
||||
dump/0,
|
||||
purge/0,
|
||||
limits/0]).
|
||||
limits/0
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
-export([
|
||||
init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2]).
|
||||
handle_info/2
|
||||
]).
|
||||
|
||||
-define(LICENSE_TAB, emqx_license).
|
||||
|
||||
|
@ -71,10 +75,14 @@ purge() ->
|
|||
init([LicenseFetcher, CheckInterval]) ->
|
||||
case LicenseFetcher() of
|
||||
{ok, License} ->
|
||||
?LICENSE_TAB = ets:new(?LICENSE_TAB, [set, protected, named_table, {read_concurrency, true}]),
|
||||
?LICENSE_TAB = ets:new(?LICENSE_TAB, [
|
||||
set, protected, named_table, {read_concurrency, true}
|
||||
]),
|
||||
#{} = check_license(License),
|
||||
State0 = ensure_check_license_timer(#{check_license_interval => CheckInterval,
|
||||
license => License}),
|
||||
State0 = ensure_check_license_timer(#{
|
||||
check_license_interval => CheckInterval,
|
||||
license => License
|
||||
}),
|
||||
State = ensure_check_expiry_timer(State0),
|
||||
{ok, State};
|
||||
{error, Reason} ->
|
||||
|
@ -100,12 +108,10 @@ handle_info(check_license, #{license := License} = State) ->
|
|||
NewState = ensure_check_license_timer(State),
|
||||
?tp(debug, emqx_license_checked, #{}),
|
||||
{noreply, NewState};
|
||||
|
||||
handle_info(check_expiry_alarm, #{license := License} = State) ->
|
||||
_ = expiry_early_alarm(License),
|
||||
NewState = ensure_check_expiry_timer(State),
|
||||
{noreply, NewState};
|
||||
|
||||
handle_info(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
|
@ -123,7 +129,8 @@ ensure_check_expiry_timer(State) ->
|
|||
State#{expiry_alarm_timer => Ref}.
|
||||
|
||||
cancel_timer(State, Key) ->
|
||||
_ = case maps:find(Key, State) of
|
||||
_ =
|
||||
case maps:find(Key, State) of
|
||||
{ok, Ref} when is_reference(Ref) -> erlang:cancel_timer(Ref);
|
||||
_ -> ok
|
||||
end,
|
||||
|
@ -134,12 +141,15 @@ check_license(License) ->
|
|||
NeedRestrict = need_restrict(License, DaysLeft),
|
||||
Limits = limits(License, NeedRestrict),
|
||||
true = apply_limits(Limits),
|
||||
#{warn_evaluation => warn_evaluation(License, NeedRestrict),
|
||||
warn_expiry => warn_expiry(License, NeedRestrict)}.
|
||||
#{
|
||||
warn_evaluation => warn_evaluation(License, NeedRestrict),
|
||||
warn_expiry => warn_expiry(License, NeedRestrict)
|
||||
}.
|
||||
|
||||
warn_evaluation(License, false) ->
|
||||
emqx_license_parser:customer_type(License) == ?EVALUATION_CUSTOMER;
|
||||
warn_evaluation(_License, _NeedRestrict) -> false.
|
||||
warn_evaluation(_License, _NeedRestrict) ->
|
||||
false.
|
||||
|
||||
warn_expiry(_License, NeedRestrict) -> NeedRestrict.
|
||||
|
||||
|
@ -155,12 +165,15 @@ need_restrict(License, DaysLeft)->
|
|||
CType = emqx_license_parser:customer_type(License),
|
||||
Type = emqx_license_parser:license_type(License),
|
||||
|
||||
DaysLeft < 0
|
||||
andalso (Type =/= ?OFFICIAL) orelse small_customer_over_expired(CType, DaysLeft).
|
||||
DaysLeft < 0 andalso
|
||||
(Type =/= ?OFFICIAL) orelse small_customer_over_expired(CType, DaysLeft).
|
||||
|
||||
small_customer_over_expired(?SMALL_CUSTOMER, DaysLeft)
|
||||
when DaysLeft < ?EXPIRED_DAY -> true;
|
||||
small_customer_over_expired(_CType, _DaysLeft) -> false.
|
||||
small_customer_over_expired(?SMALL_CUSTOMER, DaysLeft) when
|
||||
DaysLeft < ?EXPIRED_DAY
|
||||
->
|
||||
true;
|
||||
small_customer_over_expired(_CType, _DaysLeft) ->
|
||||
false.
|
||||
|
||||
apply_limits(Limits) ->
|
||||
ets:insert(?LICENSE_TAB, {limits, Limits}).
|
||||
|
|
|
@ -26,36 +26,41 @@ license(["reload"]) ->
|
|||
#{key := _Key} ->
|
||||
?PRINT_MSG("License is not configured as a file, please specify file explicitly~n")
|
||||
end;
|
||||
|
||||
license(["reload", Filename]) ->
|
||||
case emqx_license:update_file(Filename) of
|
||||
{ok, Warnings} ->
|
||||
ok = print_warnings(Warnings),
|
||||
ok = ?PRINT_MSG("ok~n");
|
||||
{error, Reason} -> ?PRINT("Error: ~p~n", [Reason])
|
||||
{error, Reason} ->
|
||||
?PRINT("Error: ~p~n", [Reason])
|
||||
end;
|
||||
|
||||
license(["update", EncodedLicense]) ->
|
||||
case emqx_license:update_key(EncodedLicense) of
|
||||
{ok, Warnings} ->
|
||||
ok = print_warnings(Warnings),
|
||||
ok = ?PRINT_MSG("ok~n");
|
||||
{error, Reason} -> ?PRINT("Error: ~p~n", [Reason])
|
||||
{error, Reason} ->
|
||||
?PRINT("Error: ~p~n", [Reason])
|
||||
end;
|
||||
|
||||
license(["info"]) ->
|
||||
lists:foreach(fun({K, V}) when is_binary(V); is_atom(V); is_list(V) ->
|
||||
lists:foreach(
|
||||
fun
|
||||
({K, V}) when is_binary(V); is_atom(V); is_list(V) ->
|
||||
?PRINT("~-16s: ~s~n", [K, V]);
|
||||
({K, V}) ->
|
||||
?PRINT("~-16s: ~p~n", [K, V])
|
||||
end, emqx_license_checker:dump());
|
||||
|
||||
end,
|
||||
emqx_license_checker:dump()
|
||||
);
|
||||
license(_) ->
|
||||
emqx_ctl:usage(
|
||||
[ {"license info", "Show license info"},
|
||||
{"license reload [<File>]", "Reload license from a file specified with an absolute path"},
|
||||
[
|
||||
{"license info", "Show license info"},
|
||||
{"license reload [<File>]",
|
||||
"Reload license from a file specified with an absolute path"},
|
||||
{"license update License", "Update license given as a string"}
|
||||
]).
|
||||
]
|
||||
).
|
||||
|
||||
unload() ->
|
||||
ok = emqx_ctl:unregister_command(license).
|
||||
|
@ -70,8 +75,10 @@ print_warnings(Warnings) ->
|
|||
|
||||
print_evaluation_warning(#{warn_evaluation := true}) ->
|
||||
?PRINT_MSG(?EVALUATION_LOG);
|
||||
print_evaluation_warning(_) -> ok.
|
||||
print_evaluation_warning(_) ->
|
||||
ok.
|
||||
|
||||
print_expiry_warning(#{warn_expiry := true}) ->
|
||||
?PRINT_MSG(?EXPIRY_LOG);
|
||||
print_expiry_warning(_) -> ok.
|
||||
print_expiry_warning(_) ->
|
||||
ok.
|
||||
|
|
|
@ -7,14 +7,18 @@
|
|||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/1,
|
||||
start_link/4]).
|
||||
-export([
|
||||
start_link/1,
|
||||
start_link/4
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
-export([
|
||||
init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2]).
|
||||
handle_info/2
|
||||
]).
|
||||
|
||||
-define(NAME, emqx).
|
||||
-define(INTERVAL, 5000).
|
||||
|
@ -35,7 +39,8 @@ start_link(Name, ServerName, Interval, Callback) ->
|
|||
|
||||
init([Name, Interval, Callback]) ->
|
||||
Pid = whereis(Name),
|
||||
State = #{interval => Interval,
|
||||
State = #{
|
||||
interval => Interval,
|
||||
name => Name,
|
||||
pid => Pid,
|
||||
callback => Callback
|
||||
|
@ -51,7 +56,6 @@ handle_cast(_Msg, State) ->
|
|||
handle_info({timeout, Timer, check_pid}, #{timer := Timer} = State) ->
|
||||
NewState = check_pid(State),
|
||||
{noreply, ensure_timer(NewState)};
|
||||
|
||||
handle_info(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
|
@ -60,7 +64,8 @@ handle_info(_Msg, State) ->
|
|||
%%------------------------------------------------------------------------------
|
||||
|
||||
ensure_timer(#{interval := Interval} = State) ->
|
||||
_ = case State of
|
||||
_ =
|
||||
case State of
|
||||
#{timer := Timer} -> erlang:cancel_timer(Timer);
|
||||
_ -> ok
|
||||
end,
|
||||
|
|
|
@ -9,33 +9,38 @@
|
|||
-include_lib("emqx/include/logger.hrl").
|
||||
-include("emqx_license.hrl").
|
||||
|
||||
-define(PUBKEY, <<"""
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbtkdos3TZmSv+D7+X5pc0yfcjum2
|
||||
Q1DK6PCWkiQihjvjJjKFzdYzcWOgC6f4Ou3mgGAUSjdQYYnFKZ/9f5ax4g==
|
||||
-----END PUBLIC KEY-----
|
||||
""">>).
|
||||
-define(PUBKEY, <<
|
||||
""
|
||||
"\n"
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbtkdos3TZmSv+D7+X5pc0yfcjum2\n"
|
||||
"Q1DK6PCWkiQihjvjJjKFzdYzcWOgC6f4Ou3mgGAUSjdQYYnFKZ/9f5ax4g==\n"
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
""
|
||||
>>).
|
||||
|
||||
-define(LICENSE_PARSE_MODULES, [emqx_license_parser_v20220101
|
||||
]).
|
||||
-define(LICENSE_PARSE_MODULES, [emqx_license_parser_v20220101]).
|
||||
|
||||
-type license_data() :: term().
|
||||
-type customer_type() :: ?SMALL_CUSTOMER |
|
||||
?MEDIUM_CUSTOMER |
|
||||
?LARGE_CUSTOMER |
|
||||
?EVALUATION_CUSTOMER.
|
||||
-type customer_type() ::
|
||||
?SMALL_CUSTOMER
|
||||
| ?MEDIUM_CUSTOMER
|
||||
| ?LARGE_CUSTOMER
|
||||
| ?EVALUATION_CUSTOMER.
|
||||
|
||||
-type license_type() :: ?OFFICIAL | ?TRIAL.
|
||||
|
||||
-type license() :: #{module := module(), data := license_data()}.
|
||||
|
||||
-export_type([license_data/0,
|
||||
-export_type([
|
||||
license_data/0,
|
||||
customer_type/0,
|
||||
license_type/0,
|
||||
license/0]).
|
||||
license/0
|
||||
]).
|
||||
|
||||
|
||||
-export([parse/1,
|
||||
-export([
|
||||
parse/1,
|
||||
parse/2,
|
||||
dump/1,
|
||||
customer_type/1,
|
||||
|
@ -99,7 +104,6 @@ max_connections(#{module := Module, data := LicenseData}) ->
|
|||
|
||||
do_parse(_Content, _Key, [], Errors) ->
|
||||
{error, lists:reverse(Errors)};
|
||||
|
||||
do_parse(Content, Key, [Module | Modules], Errors) ->
|
||||
try Module:parse(Content, Key) of
|
||||
{ok, LicenseData} ->
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
%% allow it to start from Nov.2021
|
||||
-define(MIN_START_DATE, 20211101).
|
||||
|
||||
-export([parse/2,
|
||||
-export([
|
||||
parse/2,
|
||||
dump/1,
|
||||
customer_type/1,
|
||||
license_type/1,
|
||||
expiry_date/1,
|
||||
max_connections/1]).
|
||||
max_connections/1
|
||||
]).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% API
|
||||
|
@ -40,25 +42,30 @@ parse(Content, Key) ->
|
|||
{error, Reason}
|
||||
end.
|
||||
|
||||
dump(#{type := Type,
|
||||
dump(
|
||||
#{
|
||||
type := Type,
|
||||
customer_type := CType,
|
||||
customer := Customer,
|
||||
email := Email,
|
||||
date_start := DateStart,
|
||||
max_connections := MaxConns} = License) ->
|
||||
|
||||
max_connections := MaxConns
|
||||
} = License
|
||||
) ->
|
||||
DateExpiry = expiry_date(License),
|
||||
{DateNow, _} = calendar:universal_time(),
|
||||
Expiry = DateNow > DateExpiry,
|
||||
|
||||
[{customer, Customer},
|
||||
[
|
||||
{customer, Customer},
|
||||
{email, Email},
|
||||
{max_connections, MaxConns},
|
||||
{start_at, format_date(DateStart)},
|
||||
{expiry_at, format_date(DateExpiry)},
|
||||
{type, format_type(Type)},
|
||||
{customer_type, CType},
|
||||
{expiry, Expiry}].
|
||||
{expiry, Expiry}
|
||||
].
|
||||
|
||||
customer_type(#{customer_type := CType}) -> CType.
|
||||
|
||||
|
@ -66,7 +73,8 @@ license_type(#{type := Type}) -> Type.
|
|||
|
||||
expiry_date(#{date_start := DateStart, days := Days}) ->
|
||||
calendar:gregorian_days_to_date(
|
||||
calendar:date_to_gregorian_days(DateStart) + Days).
|
||||
calendar:date_to_gregorian_days(DateStart) + Days
|
||||
).
|
||||
|
||||
max_connections(#{max_connections := MaxConns}) ->
|
||||
MaxConns.
|
||||
|
@ -92,16 +100,19 @@ verify_signature(Payload, Signature, Key) ->
|
|||
parse_payload(Payload) ->
|
||||
Lines = lists:map(
|
||||
fun string:trim/1,
|
||||
string:split(string:trim(Payload), <<"\n">>, all)),
|
||||
string:split(string:trim(Payload), <<"\n">>, all)
|
||||
),
|
||||
case Lines of
|
||||
[?LICENSE_VERSION, Type, CType, Customer, Email, DateStart, Days, MaxConns] ->
|
||||
collect_fields([{type, parse_type(Type)},
|
||||
collect_fields([
|
||||
{type, parse_type(Type)},
|
||||
{customer_type, parse_customer_type(CType)},
|
||||
{customer, {ok, Customer}},
|
||||
{email, {ok, Email}},
|
||||
{date_start, parse_date_start(DateStart)},
|
||||
{days, parse_days(Days)},
|
||||
{max_connections, parse_max_connections(MaxConns)}]);
|
||||
{max_connections, parse_max_connections(MaxConns)}
|
||||
]);
|
||||
[_Version, _Type, _CType, _Customer, _Email, _DateStart, _Days, _MaxConns] ->
|
||||
{error, invalid_version};
|
||||
_ ->
|
||||
|
@ -155,13 +166,15 @@ parse_int(Str0) ->
|
|||
|
||||
collect_fields(Fields) ->
|
||||
Collected = lists:foldl(
|
||||
fun({Name, {ok, Value}}, {FieldValues, Errors}) ->
|
||||
fun
|
||||
({Name, {ok, Value}}, {FieldValues, Errors}) ->
|
||||
{[{Name, Value} | FieldValues], Errors};
|
||||
({Name, {error, Reason}}, {FieldValues, Errors}) ->
|
||||
{FieldValues, [{Name, Reason} | Errors]}
|
||||
end,
|
||||
{[], []},
|
||||
Fields),
|
||||
Fields
|
||||
),
|
||||
case Collected of
|
||||
{FieldValues, []} ->
|
||||
{ok, maps:from_list(FieldValues)};
|
||||
|
@ -173,7 +186,9 @@ format_date({Year, Month, Day}) ->
|
|||
iolist_to_binary(
|
||||
io_lib:format(
|
||||
"~4..0w-~2..0w-~2..0w",
|
||||
[Year, Month, Day])).
|
||||
[Year, Month, Day]
|
||||
)
|
||||
).
|
||||
|
||||
format_type(?OFFICIAL) -> <<"official">>;
|
||||
format_type(?TRIAL) -> <<"trial">>.
|
||||
|
|
|
@ -10,18 +10,22 @@
|
|||
|
||||
-define(CHECK_INTERVAL, 5000).
|
||||
|
||||
-export([start_link/0,
|
||||
-export([
|
||||
start_link/0,
|
||||
start_link/1,
|
||||
local_connection_count/0,
|
||||
connection_count/0]).
|
||||
connection_count/0
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
-export([
|
||||
init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3]).
|
||||
code_change/3
|
||||
]).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% API
|
||||
|
@ -83,14 +87,17 @@ connection_quota_early_alarm({ok, #{max_connections := Max}}) when is_integer(Ma
|
|||
if
|
||||
Count > Max * High ->
|
||||
HighPercent = float_to_binary(High * 100, [{decimals, 0}]),
|
||||
Message = iolist_to_binary(["License: live connection number exceeds ", HighPercent, "%"]),
|
||||
Message = iolist_to_binary([
|
||||
"License: live connection number exceeds ", HighPercent, "%"
|
||||
]),
|
||||
catch emqx_alarm:activate(license_quota, #{high_watermark => HighPercent}, Message);
|
||||
Count < Max * Low ->
|
||||
catch emqx_alarm:deactivate(license_quota);
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
connection_quota_early_alarm(_Limits) -> ok.
|
||||
connection_quota_early_alarm(_Limits) ->
|
||||
ok.
|
||||
|
||||
cached_remote_connection_count() ->
|
||||
try ets:lookup(?MODULE, remote_connection_count) of
|
||||
|
@ -104,7 +111,8 @@ update_resources() ->
|
|||
ets:insert(?MODULE, {remote_connection_count, remote_connection_count()}).
|
||||
|
||||
ensure_timer(#{check_peer_interval := CheckInterval} = State) ->
|
||||
_ = case State of
|
||||
_ =
|
||||
case State of
|
||||
#{timer := Timer} -> erlang:cancel_timer(Timer);
|
||||
_ -> ok
|
||||
end,
|
||||
|
|
|
@ -14,37 +14,56 @@
|
|||
|
||||
-export([roots/0, fields/1, validations/0]).
|
||||
|
||||
roots() -> [{license,
|
||||
hoconsc:mk(hoconsc:union([hoconsc:ref(?MODULE, key_license),
|
||||
hoconsc:ref(?MODULE, file_license)]),
|
||||
#{desc => """EMQX Enterprise license.
|
||||
A license is either a `key` or a `file`.
|
||||
When `key` and `file` are both configured, `key` is used.
|
||||
|
||||
EMQX by default starts with a trial license. For a different license,
|
||||
visit https://www.emqx.com/apply-licenses/emqx to apply.
|
||||
"})}
|
||||
roots() ->
|
||||
[
|
||||
{license,
|
||||
hoconsc:mk(
|
||||
hoconsc:union([
|
||||
hoconsc:ref(?MODULE, key_license),
|
||||
hoconsc:ref(?MODULE, file_license)
|
||||
]),
|
||||
#{
|
||||
desc =>
|
||||
"EMQX Enterprise license.\n"
|
||||
"A license is either a `key` or a `file`.\n"
|
||||
"When `key` and `file` are both configured, `key` is used.\n"
|
||||
"\n"
|
||||
"EMQX by default starts with a trial license. For a different license,\n"
|
||||
"visit https://www.emqx.com/apply-licenses/emqx to apply.\n"
|
||||
}
|
||||
)}
|
||||
].
|
||||
|
||||
fields(key_license) ->
|
||||
[ {key, #{type => string(),
|
||||
sensitive => true, %% so it's not logged
|
||||
[
|
||||
{key, #{
|
||||
type => string(),
|
||||
%% so it's not logged
|
||||
sensitive => true,
|
||||
desc => "Configure the license as a string"
|
||||
}}
|
||||
| common_fields()];
|
||||
| common_fields()
|
||||
];
|
||||
fields(file_license) ->
|
||||
[ {file, #{type => string(),
|
||||
[
|
||||
{file, #{
|
||||
type => string(),
|
||||
desc => "Path to the license file"
|
||||
}}
|
||||
| common_fields()].
|
||||
| common_fields()
|
||||
].
|
||||
|
||||
common_fields() ->
|
||||
[
|
||||
{connection_low_watermark, #{type => emqx_schema:percent(),
|
||||
default => "75%", desc => ""
|
||||
{connection_low_watermark, #{
|
||||
type => emqx_schema:percent(),
|
||||
default => "75%",
|
||||
desc => ""
|
||||
}},
|
||||
{connection_high_watermark, #{type => emqx_schema:percent(),
|
||||
default => "80%", desc => ""
|
||||
{connection_high_watermark, #{
|
||||
type => emqx_schema:percent(),
|
||||
default => "80%",
|
||||
desc => ""
|
||||
}}
|
||||
].
|
||||
|
||||
|
@ -53,7 +72,8 @@ validations() ->
|
|||
|
||||
check_license_watermark(Conf) ->
|
||||
case hocon_maps:get("license.connection_low_watermark", Conf) of
|
||||
undefined -> true;
|
||||
undefined ->
|
||||
true;
|
||||
Low ->
|
||||
High = hocon_maps:get("license.connection_high_watermark", Conf),
|
||||
case High =/= undefined andalso High > Low of
|
||||
|
|
|
@ -16,28 +16,40 @@ start_link() ->
|
|||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
{ok, {#{strategy => one_for_one,
|
||||
{ok,
|
||||
{
|
||||
#{
|
||||
strategy => one_for_one,
|
||||
intensity => 10,
|
||||
period => 100},
|
||||
period => 100
|
||||
},
|
||||
|
||||
[#{id => license_checker,
|
||||
[
|
||||
#{
|
||||
id => license_checker,
|
||||
start => {emqx_license_checker, start_link, [fun emqx_license:read_license/0]},
|
||||
restart => permanent,
|
||||
shutdown => 5000,
|
||||
type => worker,
|
||||
modules => [emqx_license_checker]},
|
||||
modules => [emqx_license_checker]
|
||||
},
|
||||
|
||||
#{id => license_resources,
|
||||
#{
|
||||
id => license_resources,
|
||||
start => {emqx_license_resources, start_link, []},
|
||||
restart => permanent,
|
||||
shutdown => 5000,
|
||||
type => worker,
|
||||
modules => [emqx_license_resources]},
|
||||
modules => [emqx_license_resources]
|
||||
},
|
||||
|
||||
#{id => license_installer,
|
||||
#{
|
||||
id => license_installer,
|
||||
start => {emqx_license_installer, start_link, [fun emqx_license:load/0]},
|
||||
restart => permanent,
|
||||
shutdown => 5000,
|
||||
type => worker,
|
||||
modules => [emqx_license_installer]}
|
||||
]}}.
|
||||
modules => [emqx_license_installer]
|
||||
}
|
||||
]
|
||||
}}.
|
||||
|
|
|
@ -51,8 +51,8 @@ set_special_configs(emqx_license) ->
|
|||
emqx_config:put([license], Config),
|
||||
RawConfig = #{<<"file">> => emqx_license_test_lib:default_license()},
|
||||
emqx_config:put_raw([<<"license">>], RawConfig);
|
||||
|
||||
set_special_configs(_) -> ok.
|
||||
set_special_configs(_) ->
|
||||
ok.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Tests
|
||||
|
@ -61,43 +61,52 @@ set_special_configs(_) -> ok.
|
|||
t_update_file(_Config) ->
|
||||
?assertMatch(
|
||||
{error, {invalid_license_file, enoent}},
|
||||
emqx_license:update_file("/unknown/path")),
|
||||
emqx_license:update_file("/unknown/path")
|
||||
),
|
||||
|
||||
ok = file:write_file("license_with_invalid_content.lic", <<"bad license">>),
|
||||
?assertMatch(
|
||||
{error, [_ | _]},
|
||||
emqx_license:update_file("license_with_invalid_content.lic")),
|
||||
emqx_license:update_file("license_with_invalid_content.lic")
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, #{}},
|
||||
emqx_license:update_file(emqx_license_test_lib:default_license())).
|
||||
emqx_license:update_file(emqx_license_test_lib:default_license())
|
||||
).
|
||||
|
||||
t_update_value(_Config) ->
|
||||
?assertMatch(
|
||||
{error, [_ | _]},
|
||||
emqx_license:update_key("invalid.license")),
|
||||
emqx_license:update_key("invalid.license")
|
||||
),
|
||||
|
||||
{ok, LicenseValue} = file:read_file(emqx_license_test_lib:default_license()),
|
||||
|
||||
?assertMatch(
|
||||
{ok, #{}},
|
||||
emqx_license:update_key(LicenseValue)).
|
||||
emqx_license:update_key(LicenseValue)
|
||||
).
|
||||
|
||||
t_read_license_from_invalid_file(_Config) ->
|
||||
?assertMatch(
|
||||
{error, enoent},
|
||||
emqx_license:read_license()).
|
||||
emqx_license:read_license()
|
||||
).
|
||||
|
||||
t_check_exceeded(_Config) ->
|
||||
License = mk_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100000",
|
||||
"10"]),
|
||||
"10"
|
||||
]
|
||||
),
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
ok = lists:foreach(
|
||||
|
@ -105,22 +114,27 @@ t_check_exceeded(_Config) ->
|
|||
{ok, C} = emqtt:start_link(),
|
||||
{ok, _} = emqtt:connect(C)
|
||||
end,
|
||||
lists:seq(1, 12)),
|
||||
lists:seq(1, 12)
|
||||
),
|
||||
|
||||
?assertEqual(
|
||||
{stop, {error, ?RC_QUOTA_EXCEEDED}},
|
||||
emqx_license:check(#{}, #{})).
|
||||
emqx_license:check(#{}, #{})
|
||||
).
|
||||
|
||||
t_check_ok(_Config) ->
|
||||
License = mk_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100000",
|
||||
"10"]),
|
||||
"10"
|
||||
]
|
||||
),
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
ok = lists:foreach(
|
||||
|
@ -128,33 +142,43 @@ t_check_ok(_Config) ->
|
|||
{ok, C} = emqtt:start_link(),
|
||||
{ok, _} = emqtt:connect(C)
|
||||
end,
|
||||
lists:seq(1, 11)),
|
||||
lists:seq(1, 11)
|
||||
),
|
||||
|
||||
?assertEqual(
|
||||
{ok, #{}},
|
||||
emqx_license:check(#{}, #{})).
|
||||
emqx_license:check(#{}, #{})
|
||||
).
|
||||
|
||||
t_check_expired(_Config) ->
|
||||
License = mk_license(
|
||||
["220111",
|
||||
"1", %% Official customer
|
||||
"0", %% Small customer
|
||||
[
|
||||
"220111",
|
||||
%% Official customer
|
||||
"1",
|
||||
%% Small customer
|
||||
"0",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20211101", %% Expired long ago
|
||||
%% Expired long ago
|
||||
"20211101",
|
||||
"10",
|
||||
"10"]),
|
||||
"10"
|
||||
]
|
||||
),
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
?assertEqual(
|
||||
{stop, {error, ?RC_QUOTA_EXCEEDED}},
|
||||
emqx_license:check(#{}, #{})).
|
||||
emqx_license:check(#{}, #{})
|
||||
).
|
||||
|
||||
t_check_not_loaded(_Config) ->
|
||||
ok = emqx_license_checker:purge(),
|
||||
?assertEqual(
|
||||
{stop, {error, ?RC_QUOTA_EXCEEDED}},
|
||||
emqx_license:check(#{}, #{})).
|
||||
emqx_license:check(#{}, #{})
|
||||
).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Helpers
|
||||
|
@ -164,5 +188,6 @@ mk_license(Fields) ->
|
|||
EncodedLicense = emqx_license_test_lib:make_license(Fields),
|
||||
{ok, License} = emqx_license_parser:parse(
|
||||
EncodedLicense,
|
||||
emqx_license_test_lib:public_key_pem()),
|
||||
emqx_license_test_lib:public_key_pem()
|
||||
),
|
||||
License.
|
||||
|
|
|
@ -25,22 +25,20 @@ end_per_suite(_) ->
|
|||
init_per_testcase(t_default_limits, Config) ->
|
||||
ok = emqx_common_test_helpers:stop_apps([emqx_license]),
|
||||
Config;
|
||||
|
||||
init_per_testcase(_Case, Config) ->
|
||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
||||
Config.
|
||||
|
||||
end_per_testcase(t_default_limits, _Config) ->
|
||||
ok = emqx_common_test_helpers:start_apps([emqx_license], fun set_special_configs/1);
|
||||
|
||||
end_per_testcase(_Case, _Config) ->
|
||||
ok.
|
||||
|
||||
set_special_configs(emqx_license) ->
|
||||
Config = #{file => emqx_license_test_lib:default_license()},
|
||||
emqx_config:put([license], Config);
|
||||
|
||||
set_special_configs(_) -> ok.
|
||||
set_special_configs(_) ->
|
||||
ok.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Tests
|
||||
|
@ -51,43 +49,53 @@ t_default_limits(_Config) ->
|
|||
|
||||
t_dump(_Config) ->
|
||||
License = mk_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100000",
|
||||
"10"]),
|
||||
"10"
|
||||
]
|
||||
),
|
||||
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
?assertEqual(
|
||||
[{customer,<<"Foo">>},
|
||||
[
|
||||
{customer, <<"Foo">>},
|
||||
{email, <<"contact@foo.com">>},
|
||||
{max_connections, 10},
|
||||
{start_at, <<"2022-01-11">>},
|
||||
{expiry_at, <<"2295-10-27">>},
|
||||
{type, <<"trial">>},
|
||||
{customer_type, 10},
|
||||
{expiry,false}],
|
||||
emqx_license_checker:dump()).
|
||||
{expiry, false}
|
||||
],
|
||||
emqx_license_checker:dump()
|
||||
).
|
||||
|
||||
t_update(_Config) ->
|
||||
License = mk_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100000",
|
||||
"123"]),
|
||||
"123"
|
||||
]
|
||||
),
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
?assertMatch(
|
||||
{ok, #{max_connections := 123}},
|
||||
emqx_license_checker:limits()).
|
||||
emqx_license_checker:limits()
|
||||
).
|
||||
|
||||
t_update_by_timer(_Config) ->
|
||||
?check_trace(
|
||||
|
@ -96,94 +104,117 @@ t_update_by_timer(_Config) ->
|
|||
begin
|
||||
erlang:send(
|
||||
emqx_license_checker,
|
||||
check_license)
|
||||
check_license
|
||||
)
|
||||
end,
|
||||
#{?snk_kind := emqx_license_checked},
|
||||
1000)
|
||||
1000
|
||||
)
|
||||
end,
|
||||
fun(Trace) ->
|
||||
?assertMatch([_ | _], ?of_kind(emqx_license_checked, Trace))
|
||||
end).
|
||||
end
|
||||
).
|
||||
|
||||
t_expired_trial(_Config) ->
|
||||
{NowDate, _} = calendar:universal_time(),
|
||||
Date10DaysAgo = calendar:gregorian_days_to_date(
|
||||
calendar:date_to_gregorian_days(NowDate) - 10),
|
||||
calendar:date_to_gregorian_days(NowDate) - 10
|
||||
),
|
||||
|
||||
License = mk_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
format_date(Date10DaysAgo),
|
||||
"1",
|
||||
"123"]),
|
||||
"123"
|
||||
]
|
||||
),
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
?assertMatch(
|
||||
{ok, #{max_connections := expired}},
|
||||
emqx_license_checker:limits()).
|
||||
emqx_license_checker:limits()
|
||||
).
|
||||
|
||||
t_overexpired_small_client(_Config) ->
|
||||
{NowDate, _} = calendar:universal_time(),
|
||||
Date100DaysAgo = calendar:gregorian_days_to_date(
|
||||
calendar:date_to_gregorian_days(NowDate) - 100),
|
||||
calendar:date_to_gregorian_days(NowDate) - 100
|
||||
),
|
||||
|
||||
License = mk_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"1",
|
||||
"0",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
format_date(Date100DaysAgo),
|
||||
"1",
|
||||
"123"]),
|
||||
"123"
|
||||
]
|
||||
),
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
?assertMatch(
|
||||
{ok, #{max_connections := expired}},
|
||||
emqx_license_checker:limits()).
|
||||
emqx_license_checker:limits()
|
||||
).
|
||||
|
||||
t_overexpired_medium_client(_Config) ->
|
||||
{NowDate, _} = calendar:universal_time(),
|
||||
Date100DaysAgo = calendar:gregorian_days_to_date(
|
||||
calendar:date_to_gregorian_days(NowDate) - 100),
|
||||
calendar:date_to_gregorian_days(NowDate) - 100
|
||||
),
|
||||
|
||||
License = mk_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"1",
|
||||
"1",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
format_date(Date100DaysAgo),
|
||||
"1",
|
||||
"123"]),
|
||||
"123"
|
||||
]
|
||||
),
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
?assertMatch(
|
||||
{ok, #{max_connections := 123}},
|
||||
emqx_license_checker:limits()).
|
||||
emqx_license_checker:limits()
|
||||
).
|
||||
|
||||
t_recently_expired_small_client(_Config) ->
|
||||
{NowDate, _} = calendar:universal_time(),
|
||||
Date10DaysAgo = calendar:gregorian_days_to_date(
|
||||
calendar:date_to_gregorian_days(NowDate) - 10),
|
||||
calendar:date_to_gregorian_days(NowDate) - 10
|
||||
),
|
||||
|
||||
License = mk_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"1",
|
||||
"0",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
format_date(Date10DaysAgo),
|
||||
"1",
|
||||
"123"]),
|
||||
"123"
|
||||
]
|
||||
),
|
||||
#{} = emqx_license_checker:update(License),
|
||||
|
||||
?assertMatch(
|
||||
{ok, #{max_connections := 123}},
|
||||
emqx_license_checker:limits()).
|
||||
emqx_license_checker:limits()
|
||||
).
|
||||
|
||||
t_unknown_calls(_Config) ->
|
||||
ok = gen_server:cast(emqx_license_checker, some_cast),
|
||||
|
@ -198,11 +229,14 @@ mk_license(Fields) ->
|
|||
EncodedLicense = emqx_license_test_lib:make_license(Fields),
|
||||
{ok, License} = emqx_license_parser:parse(
|
||||
EncodedLicense,
|
||||
emqx_license_test_lib:public_key_pem()),
|
||||
emqx_license_test_lib:public_key_pem()
|
||||
),
|
||||
License.
|
||||
|
||||
format_date({Year, Month, Day}) ->
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
"~4..0w~2..0w~2..0w",
|
||||
[Year, Month, Day])).
|
||||
[Year, Month, Day]
|
||||
)
|
||||
).
|
||||
|
|
|
@ -35,8 +35,8 @@ set_special_configs(emqx_license) ->
|
|||
emqx_config:put([license], Config),
|
||||
RawConfig = #{<<"file">> => emqx_license_test_lib:default_license()},
|
||||
emqx_config:put_raw([<<"license">>], RawConfig);
|
||||
|
||||
set_special_configs(_) -> ok.
|
||||
set_special_configs(_) ->
|
||||
ok.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Tests
|
||||
|
@ -58,4 +58,3 @@ t_update(_Config) ->
|
|||
_ = emqx_license_cli:license(["update", LicenseValue]),
|
||||
_ = emqx_license_cli:license(["reload"]),
|
||||
_ = emqx_license_cli:license(["update", "Invalid License Value"]).
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ end_per_testcase(_Case, _Config) ->
|
|||
set_special_configs(emqx_license) ->
|
||||
Config = #{file => emqx_license_test_lib:default_license()},
|
||||
emqx_config:put([license], Config);
|
||||
|
||||
set_special_configs(_) -> ok.
|
||||
set_special_configs(_) ->
|
||||
ok.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Tests
|
||||
|
@ -45,35 +45,43 @@ t_update(_Config) ->
|
|||
begin
|
||||
?wait_async_action(
|
||||
begin
|
||||
Pid0 = spawn_link(fun() -> receive exit -> ok end end),
|
||||
Pid0 = spawn_link(fun() ->
|
||||
receive
|
||||
exit -> ok
|
||||
end
|
||||
end),
|
||||
register(installer_test, Pid0),
|
||||
|
||||
{ok, _} = emqx_license_installer:start_link(
|
||||
installer_test,
|
||||
?MODULE,
|
||||
10,
|
||||
fun() -> ok end),
|
||||
|
||||
fun() -> ok end
|
||||
),
|
||||
|
||||
{ok, _} = ?block_until(
|
||||
#{?snk_kind := emqx_license_installer_nochange},
|
||||
100),
|
||||
100
|
||||
),
|
||||
|
||||
Pid0 ! exit,
|
||||
|
||||
{ok, _} = ?block_until(
|
||||
#{?snk_kind := emqx_license_installer_noproc},
|
||||
100),
|
||||
100
|
||||
),
|
||||
|
||||
Pid1 = spawn_link(fun() -> timer:sleep(100) end),
|
||||
register(installer_test, Pid1)
|
||||
end,
|
||||
#{?snk_kind := emqx_license_installer_called},
|
||||
1000)
|
||||
1000
|
||||
)
|
||||
end,
|
||||
fun(Trace) ->
|
||||
?assertMatch([_ | _], ?of_kind(emqx_license_installer_called, Trace))
|
||||
end).
|
||||
end
|
||||
).
|
||||
|
||||
t_unknown_calls(_Config) ->
|
||||
ok = gen_server:cast(emqx_license_installer, some_cast),
|
||||
|
|
|
@ -32,8 +32,8 @@ end_per_testcase(_Case, _Config) ->
|
|||
set_special_configs(emqx_license) ->
|
||||
Config = #{file => emqx_license_test_lib:default_license()},
|
||||
emqx_config:put([license], Config);
|
||||
|
||||
set_special_configs(_) -> ok.
|
||||
set_special_configs(_) ->
|
||||
ok.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Tests
|
||||
|
@ -44,11 +44,11 @@ t_parse(_Config) ->
|
|||
|
||||
%% invalid version
|
||||
?assertMatch(
|
||||
{error,
|
||||
[{emqx_license_parser_v20220101,invalid_version}]},
|
||||
{error, [{emqx_license_parser_v20220101, invalid_version}]},
|
||||
emqx_license_parser:parse(
|
||||
emqx_license_test_lib:make_license(
|
||||
["220101",
|
||||
[
|
||||
"220101",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
|
@ -56,36 +56,46 @@ t_parse(_Config) ->
|
|||
"20220111",
|
||||
"100000",
|
||||
"10"
|
||||
]),
|
||||
public_key_pem())),
|
||||
]
|
||||
),
|
||||
public_key_pem()
|
||||
)
|
||||
),
|
||||
|
||||
%% invalid field number
|
||||
?assertMatch(
|
||||
{error,
|
||||
[{emqx_license_parser_v20220101,invalid_field_number}]},
|
||||
{error, [{emqx_license_parser_v20220101, invalid_field_number}]},
|
||||
emqx_license_parser:parse(
|
||||
emqx_license_test_lib:make_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo", "Bar",
|
||||
"Foo",
|
||||
"Bar",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100000",
|
||||
"10"
|
||||
]),
|
||||
public_key_pem())),
|
||||
]
|
||||
),
|
||||
public_key_pem()
|
||||
)
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{error,
|
||||
[{emqx_license_parser_v20220101,
|
||||
[{type,invalid_license_type},
|
||||
{error, [
|
||||
{emqx_license_parser_v20220101, [
|
||||
{type, invalid_license_type},
|
||||
{customer_type, invalid_customer_type},
|
||||
{date_start, invalid_date},
|
||||
{days,invalid_int_value}]}]},
|
||||
{days, invalid_int_value}
|
||||
]}
|
||||
]},
|
||||
emqx_license_parser:parse(
|
||||
emqx_license_test_lib:make_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"zero",
|
||||
"ten",
|
||||
"Foo",
|
||||
|
@ -93,19 +103,25 @@ t_parse(_Config) ->
|
|||
"20220231",
|
||||
"-10",
|
||||
"10"
|
||||
]),
|
||||
public_key_pem())),
|
||||
]
|
||||
),
|
||||
public_key_pem()
|
||||
)
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{error,
|
||||
[{emqx_license_parser_v20220101,
|
||||
[{type,invalid_license_type},
|
||||
{error, [
|
||||
{emqx_license_parser_v20220101, [
|
||||
{type, invalid_license_type},
|
||||
{customer_type, invalid_customer_type},
|
||||
{date_start, invalid_date},
|
||||
{days,invalid_int_value}]}]},
|
||||
{days, invalid_int_value}
|
||||
]}
|
||||
]},
|
||||
emqx_license_parser:parse(
|
||||
emqx_license_test_lib:make_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"zero",
|
||||
"ten",
|
||||
"Foo",
|
||||
|
@ -113,66 +129,85 @@ t_parse(_Config) ->
|
|||
"2022-02-1st",
|
||||
"-10",
|
||||
"10"
|
||||
]),
|
||||
public_key_pem())),
|
||||
]
|
||||
),
|
||||
public_key_pem()
|
||||
)
|
||||
),
|
||||
|
||||
%% invalid signature
|
||||
[LicensePart, _] = binary:split(
|
||||
emqx_license_test_lib:make_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100000",
|
||||
"10"]),
|
||||
<<".">>),
|
||||
"10"
|
||||
]
|
||||
),
|
||||
<<".">>
|
||||
),
|
||||
[_, SignaturePart] = binary:split(
|
||||
emqx_license_test_lib:make_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"1",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100000",
|
||||
"10"]),
|
||||
<<".">>),
|
||||
"10"
|
||||
]
|
||||
),
|
||||
<<".">>
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{error,
|
||||
[{emqx_license_parser_v20220101,invalid_signature}]},
|
||||
{error, [{emqx_license_parser_v20220101, invalid_signature}]},
|
||||
emqx_license_parser:parse(
|
||||
iolist_to_binary([LicensePart, <<".">>, SignaturePart]),
|
||||
public_key_pem())),
|
||||
public_key_pem()
|
||||
)
|
||||
),
|
||||
|
||||
%% totally invalid strings as license
|
||||
?assertMatch(
|
||||
{error, [_ | _]},
|
||||
emqx_license_parser:parse(
|
||||
<<"badlicense">>,
|
||||
public_key_pem())),
|
||||
public_key_pem()
|
||||
)
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{error, [_ | _]},
|
||||
emqx_license_parser:parse(
|
||||
<<"bad.license">>,
|
||||
public_key_pem())).
|
||||
public_key_pem()
|
||||
)
|
||||
).
|
||||
|
||||
t_dump(_Config) ->
|
||||
{ok, License} = emqx_license_parser:parse(sample_license(), public_key_pem()),
|
||||
|
||||
?assertEqual(
|
||||
[{customer,<<"Foo">>},
|
||||
[
|
||||
{customer, <<"Foo">>},
|
||||
{email, <<"contact@foo.com">>},
|
||||
{max_connections, 10},
|
||||
{start_at, <<"2022-01-11">>},
|
||||
{expiry_at, <<"2295-10-27">>},
|
||||
{type, <<"trial">>},
|
||||
{customer_type, 10},
|
||||
{expiry,false}],
|
||||
emqx_license_parser:dump(License)).
|
||||
{expiry, false}
|
||||
],
|
||||
emqx_license_parser:dump(License)
|
||||
).
|
||||
|
||||
t_customer_type(_Config) ->
|
||||
{ok, License} = emqx_license_parser:parse(sample_license(), public_key_pem()),
|
||||
|
@ -203,11 +238,14 @@ public_key_pem() ->
|
|||
|
||||
sample_license() ->
|
||||
emqx_license_test_lib:make_license(
|
||||
["220111",
|
||||
[
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100,000",
|
||||
"10"]).
|
||||
"10"
|
||||
]
|
||||
).
|
||||
|
|
|
@ -33,8 +33,8 @@ end_per_testcase(_Case, _Config) ->
|
|||
set_special_configs(emqx_license) ->
|
||||
Config = #{file => emqx_license_test_lib:default_license()},
|
||||
emqx_config:put([license], Config);
|
||||
|
||||
set_special_configs(_) -> ok.
|
||||
set_special_configs(_) ->
|
||||
ok.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Tests
|
||||
|
@ -46,14 +46,15 @@ t_connection_count(_Config) ->
|
|||
?wait_async_action(
|
||||
whereis(emqx_license_resources) ! update_resources,
|
||||
#{?snk_kind := emqx_license_resources_updated},
|
||||
1000),
|
||||
1000
|
||||
),
|
||||
emqx_license_resources:connection_count()
|
||||
end,
|
||||
fun(ConnCount, Trace) ->
|
||||
?assertEqual(0, ConnCount),
|
||||
?assertMatch([_ | _], ?of_kind(emqx_license_resources_updated, Trace))
|
||||
end),
|
||||
|
||||
end
|
||||
),
|
||||
|
||||
meck:new(emqx_cm, [passthrough]),
|
||||
meck:expect(emqx_cm, get_connected_client_count, fun() -> 10 end),
|
||||
|
@ -64,19 +65,22 @@ t_connection_count(_Config) ->
|
|||
remote_connection_counts,
|
||||
fun(_Nodes) ->
|
||||
[{ok, 5}, {error, some_error}]
|
||||
end),
|
||||
end
|
||||
),
|
||||
|
||||
?check_trace(
|
||||
begin
|
||||
?wait_async_action(
|
||||
whereis(emqx_license_resources) ! update_resources,
|
||||
#{?snk_kind := emqx_license_resources_updated},
|
||||
1000),
|
||||
1000
|
||||
),
|
||||
emqx_license_resources:connection_count()
|
||||
end,
|
||||
fun(ConnCount, _Trace) ->
|
||||
?assertEqual(15, ConnCount)
|
||||
end),
|
||||
end
|
||||
),
|
||||
|
||||
meck:unload(emqx_license_proto_v1),
|
||||
meck:unload(emqx_cm).
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
-compile(nowarn_export_all).
|
||||
-compile(export_all).
|
||||
|
||||
-define(DEFAULT_LICENSE_VALUES,
|
||||
["220111",
|
||||
-define(DEFAULT_LICENSE_VALUES, [
|
||||
"220111",
|
||||
"0",
|
||||
"10",
|
||||
"Foo",
|
||||
"contact@foo.com",
|
||||
"20220111",
|
||||
"100000",
|
||||
"10"]).
|
||||
"10"
|
||||
]).
|
||||
|
||||
-define(DEFAULT_LICENSE_FILE, "emqx.lic").
|
||||
|
||||
|
@ -36,7 +37,8 @@ test_key(Filename, Format) ->
|
|||
Path = filename:join([Dir, "data", Filename]),
|
||||
{ok, KeyData} = file:read_file(Path),
|
||||
case Format of
|
||||
pem -> KeyData;
|
||||
pem ->
|
||||
KeyData;
|
||||
decoded ->
|
||||
[PemEntry] = public_key:pem_decode(KeyData),
|
||||
public_key:pem_entry_decode(PemEntry)
|
||||
|
|
Loading…
Reference in New Issue