diff --git a/lib-ee/emqx_license/etc/emqx_license.conf b/lib-ee/emqx_license/etc/emqx_license.conf index 6c5ad217b..476444ea0 100644 --- a/lib-ee/emqx_license/etc/emqx_license.conf +++ b/lib-ee/emqx_license/etc/emqx_license.conf @@ -1,5 +1,6 @@ license { - key = "MjIwMTExCjAKMTAKRXZhbHVhdGlvbgpjb250YWN0QGVtcXguaW8KMjAyMjAxMDEKMzY1MDAKMTAK.MEUCIFc9EUjqB3SjpRqWjqmAzI4Tg4LwhCRet9scEoxMRt8fAiEAk6vfYUiPOTzBC+3EjNF3WmLTiA3B0TN5ZNwuTKbTXJQ=" + # The default license has 1000 connections limit, it is issued on 20220419 and valid for 5 years (1825 days) + key = "MjIwMTExCjAKMTAKRXZhbHVhdGlvbgpjb250YWN0QGVtcXguaW8KZGVmYXVsdAoyMDIyMDQxOQoxODI1CjEwMDAK.MEQCICbgRVijCQov2hrvZXR1mk9Oa+tyV1F5oJ6iOZeSHjnQAiB9dUiVeaZekDOjztk+NCWjhk4PG8tWfw2uFZWruSzD6g==" connection_low_watermark = 75%, connection_high_watermark = 80% } diff --git a/lib-ee/emqx_license/include/emqx_license.hrl b/lib-ee/emqx_license/include/emqx_license.hrl index 19a0cc954..5cc7c6df0 100644 --- a/lib-ee/emqx_license/include/emqx_license.hrl +++ b/lib-ee/emqx_license/include/emqx_license.hrl @@ -9,20 +9,20 @@ -define(EVALUATION_LOG, "\n" - "===============================================================================\n" - "This is an evaluation license that is restricted to 10 concurrent connections.\n" - "If you already have a paid license, please apply it now.\n" - "Or you could visit https://emqx.com/apply-licenses/emqx to get a trial license.\n" - "===============================================================================\n" + "========================================================================\n" + "Using an evaluation license limited to ~p concurrent connections.\n" + "Apply a license at https://emqx.com/apply-licenses/emqx.\n" + "Or contact EMQ customer services.\n" + "========================================================================\n" ). -define(EXPIRY_LOG, "\n" - "======================================================\n" - "Your license has expired.\n" - "Please visit https://emqx.com/apply-licenses/emqx or\n" - "contact customer services.\n" - "======================================================\n" + "========================================================================\n" + "License has been expired for ~p days.\n" + "Apply a new license at https://emqx.com/apply-licenses/emqx.\n" + "Or contact EMQ customer services.\n" + "========================================================================\n" ). -define(OFFICIAL, 1). diff --git a/lib-ee/emqx_license/src/emqx_license_checker.erl b/lib-ee/emqx_license/src/emqx_license_checker.erl index add2927e6..161e5a9cf 100644 --- a/lib-ee/emqx_license/src/emqx_license_checker.erl +++ b/lib-ee/emqx_license/src/emqx_license_checker.erl @@ -11,6 +11,7 @@ -define(CHECK_INTERVAL, 5000). -define(EXPIRY_ALARM_CHECK_INTERVAL, 24 * 60 * 60). +-define(OK(EXPR), try _ = begin EXPR end, ok catch _:_ -> ok end). -export([ start_link/1, @@ -18,7 +19,8 @@ update/1, dump/0, purge/0, - limits/0 + limits/0, + print_warnings/1 ]). %% gen_server callbacks @@ -78,7 +80,7 @@ init([LicenseFetcher, CheckInterval]) -> ?LICENSE_TAB = ets:new(?LICENSE_TAB, [ set, protected, named_table, {read_concurrency, true} ]), - #{} = check_license(License), + ok = print_warnings(check_license(License)), State0 = ensure_check_license_timer(#{ check_license_interval => CheckInterval, license => License @@ -90,7 +92,7 @@ init([LicenseFetcher, CheckInterval]) -> end. handle_call({update, License}, _From, State) -> - _ = expiry_early_alarm(License), + ok = expiry_early_alarm(License), {reply, check_license(License), State#{license => License}}; handle_call(dump, _From, #{license := License} = State) -> {reply, emqx_license_parser:dump(License), State}; @@ -109,7 +111,7 @@ handle_info(check_license, #{license := License} = State) -> ?tp(debug, emqx_license_checked, #{}), {noreply, NewState}; handle_info(check_expiry_alarm, #{license := License} = State) -> - _ = expiry_early_alarm(License), + ok = expiry_early_alarm(License), NewState = ensure_check_expiry_timer(State), {noreply, NewState}; handle_info(_Msg, State) -> @@ -138,21 +140,21 @@ cancel_timer(State, Key) -> check_license(License) -> DaysLeft = days_left(License), - NeedRestrict = need_restrict(License, DaysLeft), - Limits = limits(License, NeedRestrict), + IsOverdue = is_overdue(License, DaysLeft), + NeedRestriction = IsOverdue, + MaxConn = emqx_license_parser:max_connections(License), + Limits = limits(License, NeedRestriction), true = apply_limits(Limits), #{ - warn_evaluation => warn_evaluation(License, NeedRestrict), - warn_expiry => warn_expiry(License, NeedRestrict) + warn_evaluation => warn_evaluation(License, NeedRestriction, MaxConn), + warn_expiry => {(DaysLeft < 0), -DaysLeft} }. -warn_evaluation(License, false) -> - emqx_license_parser:customer_type(License) == ?EVALUATION_CUSTOMER; -warn_evaluation(_License, _NeedRestrict) -> +warn_evaluation(License, false, MaxConn) -> + {emqx_license_parser:customer_type(License) == ?EVALUATION_CUSTOMER, MaxConn}; +warn_evaluation(_License, _NeedRestrict, _Limits) -> false. -warn_expiry(_License, NeedRestrict) -> NeedRestrict. - limits(License, false) -> #{max_connections => emqx_license_parser:max_connections(License)}; limits(_License, true) -> #{max_connections => ?ERR_EXPIRED}. @@ -161,19 +163,20 @@ days_left(License) -> {DateNow, _} = calendar:universal_time(), calendar:date_to_gregorian_days(DateEnd) - calendar:date_to_gregorian_days(DateNow). -need_restrict(License, DaysLeft) -> +is_overdue(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). + small_customer_overdue(CType, DaysLeft) orelse + non_official_license_overdue(Type, DaysLeft). -small_customer_over_expired(?SMALL_CUSTOMER, DaysLeft) when - DaysLeft < ?EXPIRED_DAY --> - true; -small_customer_over_expired(_CType, _DaysLeft) -> - false. +%% small customers overdue 90 days after license expiry date +small_customer_overdue(?SMALL_CUSTOMER, DaysLeft) -> DaysLeft < ?EXPIRED_DAY; +small_customer_overdue(_CType, _DaysLeft) -> false. + +%% never restrict official license +non_official_license_overdue(?OFFICIAL, _) -> false; +non_official_license_overdue(_, DaysLeft) -> DaysLeft < 0. apply_limits(Limits) -> ets:insert(?LICENSE_TAB, {limits, Limits}). @@ -181,8 +184,23 @@ apply_limits(Limits) -> expiry_early_alarm(License) -> case days_left(License) < 30 of true -> - DateEnd = emqx_license_parser:expiry_date(License), - catch emqx_alarm:activate(license_expiry, #{expiry_at => DateEnd}); + {Y, M, D} = emqx_license_parser:expiry_date(License), + Date = iolist_to_binary(io_lib:format("~B~2..0B~2..0B", [Y, M, D])), + ?OK(emqx_alarm:activate(license_expiry, #{expiry_at => Date})); false -> - catch emqx_alarm:deactivate(license_expiry) + ?OK(emqx_alarm:deactivate(license_expiry)) end. + +print_warnings(Warnings) -> + ok = print_evaluation_warning(Warnings), + ok = print_expiry_warning(Warnings). + +print_evaluation_warning(#{warn_evaluation := {true, MaxConn}}) -> + io:format(?EVALUATION_LOG, [MaxConn]); +print_evaluation_warning(_) -> + ok. + +print_expiry_warning(#{warn_expiry := {true, Days}}) -> + io:format(?EXPIRY_LOG, [Days]); +print_expiry_warning(_) -> + ok. diff --git a/lib-ee/emqx_license/src/emqx_license_cli.erl b/lib-ee/emqx_license/src/emqx_license_cli.erl index 7eccdf79a..9d618db67 100644 --- a/lib-ee/emqx_license/src/emqx_license_cli.erl +++ b/lib-ee/emqx_license/src/emqx_license_cli.erl @@ -66,19 +66,4 @@ unload() -> ok = emqx_ctl:unregister_command(license). print_warnings(Warnings) -> - ok = print_evaluation_warning(Warnings), - ok = print_expiry_warning(Warnings). - -%%------------------------------------------------------------------------------ -%% Private functions -%%------------------------------------------------------------------------------ - -print_evaluation_warning(#{warn_evaluation := true}) -> - ?PRINT_MSG(?EVALUATION_LOG); -print_evaluation_warning(_) -> - ok. - -print_expiry_warning(#{warn_expiry := true}) -> - ?PRINT_MSG(?EXPIRY_LOG); -print_expiry_warning(_) -> - ok. + emqx_license_checker:print_warnings(Warnings). diff --git a/lib-ee/emqx_license/src/emqx_license_parser_legacy.erl b/lib-ee/emqx_license/src/emqx_license_parser_legacy.erl index 894d5d8aa..a5fa4ab64 100644 --- a/lib-ee/emqx_license/src/emqx_license_parser_legacy.erl +++ b/lib-ee/emqx_license/src/emqx_license_parser_legacy.erl @@ -89,6 +89,7 @@ dump(#{ [ {customer, Customer}, {email, Email}, + {deployment, "default"}, {max_connections, MaxConnections}, {start_at, format_date(StartAtDate)}, {expiry_at, format_date(ExpiryAtDate)}, diff --git a/lib-ee/emqx_license/src/emqx_license_parser_v20220101.erl b/lib-ee/emqx_license/src/emqx_license_parser_v20220101.erl index bafb02d5d..d721e3de0 100644 --- a/lib-ee/emqx_license/src/emqx_license_parser_v20220101.erl +++ b/lib-ee/emqx_license/src/emqx_license_parser_v20220101.erl @@ -48,6 +48,7 @@ dump( customer_type := CType, customer := Customer, email := Email, + deployment := Deployment, date_start := DateStart, max_connections := MaxConns } = License @@ -59,6 +60,7 @@ dump( [ {customer, Customer}, {email, Email}, + {deployment, Deployment}, {max_connections, MaxConns}, {start_at, format_date(DateStart)}, {expiry_at, format_date(DateExpiry)}, @@ -103,20 +105,21 @@ parse_payload(Payload) -> string:split(string:trim(Payload), <<"\n">>, all) ), case Lines of - [?LICENSE_VERSION, Type, CType, Customer, Email, DateStart, Days, MaxConns] -> + [?LICENSE_VERSION, Type, CType, Customer, Email, Deployment, DateStart, Days, MaxConns] -> collect_fields([ {type, parse_type(Type)}, {customer_type, parse_customer_type(CType)}, {customer, {ok, Customer}}, {email, {ok, Email}}, + {deployment, {ok, Deployment}}, {date_start, parse_date_start(DateStart)}, {days, parse_days(Days)}, {max_connections, parse_max_connections(MaxConns)} ]); - [_Version, _Type, _CType, _Customer, _Email, _DateStart, _Days, _MaxConns] -> + [_Version, _Type, _CType, _Customer, _Email, _Deployment, _DateStart, _Days, _MaxConns] -> {error, invalid_version}; _ -> - {error, invalid_field_number} + {error, unexpected_number_of_fields} end. parse_type(TypeStr) -> diff --git a/lib-ee/emqx_license/test/emqx_license_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_SUITE.erl index 4744a1cef..bc73f9071 100644 --- a/lib-ee/emqx_license/test/emqx_license_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_SUITE.erl @@ -102,6 +102,7 @@ t_check_exceeded(_Config) -> "10", "Foo", "contact@foo.com", + "bar", "20220111", "100000", "10" @@ -130,6 +131,7 @@ t_check_ok(_Config) -> "10", "Foo", "contact@foo.com", + "bar", "20220111", "100000", "10" @@ -160,6 +162,7 @@ t_check_expired(_Config) -> "0", "Foo", "contact@foo.com", + "bar", %% Expired long ago "20211101", "10", diff --git a/lib-ee/emqx_license/test/emqx_license_checker_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_checker_SUITE.erl index dcf2a0197..8842db7f9 100644 --- a/lib-ee/emqx_license/test/emqx_license_checker_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_checker_SUITE.erl @@ -55,6 +55,7 @@ t_dump(_Config) -> "10", "Foo", "contact@foo.com", + "bar", "20220111", "100000", "10" @@ -67,6 +68,7 @@ t_dump(_Config) -> [ {customer, <<"Foo">>}, {email, <<"contact@foo.com">>}, + {deployment, <<"bar">>}, {max_connections, 10}, {start_at, <<"2022-01-11">>}, {expiry_at, <<"2295-10-27">>}, @@ -85,6 +87,7 @@ t_update(_Config) -> "10", "Foo", "contact@foo.com", + "bar", "20220111", "100000", "123" @@ -129,6 +132,7 @@ t_expired_trial(_Config) -> "10", "Foo", "contact@foo.com", + "bar", format_date(Date10DaysAgo), "1", "123" @@ -154,6 +158,7 @@ t_overexpired_small_client(_Config) -> "0", "Foo", "contact@foo.com", + "bar", format_date(Date100DaysAgo), "1", "123" @@ -179,6 +184,7 @@ t_overexpired_medium_client(_Config) -> "1", "Foo", "contact@foo.com", + "bar", format_date(Date100DaysAgo), "1", "123" @@ -204,6 +210,7 @@ t_recently_expired_small_client(_Config) -> "0", "Foo", "contact@foo.com", + "bar", format_date(Date10DaysAgo), "1", "123" diff --git a/lib-ee/emqx_license/test/emqx_license_parser_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_parser_SUITE.erl index a1633d17f..3b4e78a49 100644 --- a/lib-ee/emqx_license/test/emqx_license_parser_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_parser_SUITE.erl @@ -51,6 +51,7 @@ t_parse(_Config) -> "10", "Foo", "contact@foo.com", + "bar-deployment", "20220111", "100000", "10" @@ -73,8 +74,10 @@ t_parse(_Config) -> "0", "10", "Foo", + % one extra field "Bar", "contact@foo.com", + "default-deployment", "20220111", "100000", "10" @@ -85,7 +88,7 @@ t_parse(_Config) -> ?assertMatch({error, _}, Res2), {error, Err2} = Res2, ?assertEqual( - invalid_field_number, + unexpected_number_of_fields, proplists:get_value(emqx_license_parser_v20220101, Err2) ), @@ -97,6 +100,7 @@ t_parse(_Config) -> "ten", "Foo", "contact@foo.com", + "default-deployment", "20220231", "-10", "10" @@ -124,6 +128,7 @@ t_parse(_Config) -> "ten", "Foo", "contact@foo.com", + "default-deployment", "2022-02-1st", "-10", "10" @@ -133,6 +138,7 @@ t_parse(_Config) -> ), ?assertMatch({error, _}, Res4), {error, Err4} = Res4, + ?assertEqual( [ {type, invalid_license_type}, @@ -152,6 +158,7 @@ t_parse(_Config) -> "10", "Foo", "contact@foo.com", + "default-deployment", "20220111", "100000", "10" @@ -167,6 +174,7 @@ t_parse(_Config) -> "10", "Foo", "contact@foo.com", + "default-deployment", "20220111", "100000", "10" @@ -210,6 +218,7 @@ t_dump(_Config) -> [ {customer, <<"Foo">>}, {email, <<"contact@foo.com">>}, + {deployment, <<"default-deployment">>}, {max_connections, 10}, {start_at, <<"2022-01-11">>}, {expiry_at, <<"2295-10-27">>}, @@ -255,6 +264,7 @@ sample_license() -> "10", "Foo", "contact@foo.com", + "default-deployment", "20220111", "100,000", "10" diff --git a/lib-ee/emqx_license/test/emqx_license_parser_legacy_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_parser_legacy_SUITE.erl index 02b842d94..b1c45ade7 100644 --- a/lib-ee/emqx_license/test/emqx_license_parser_legacy_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_parser_legacy_SUITE.erl @@ -58,6 +58,7 @@ t_dump(_Config) -> [ {customer, <<"EMQ X Evaluation">>}, {email, "contact@emqx.io"}, + {deployment, "default"}, {max_connections, 10}, {start_at, <<"2020-06-20">>}, {expiry_at, <<"2049-01-01">>}, diff --git a/lib-ee/emqx_license/test/emqx_license_test_lib.erl b/lib-ee/emqx_license/test/emqx_license_test_lib.erl index bf0ea6e66..d3f2b5bd7 100644 --- a/lib-ee/emqx_license/test/emqx_license_test_lib.erl +++ b/lib-ee/emqx_license/test/emqx_license_test_lib.erl @@ -58,7 +58,7 @@ make_license(Values) -> default_license() -> %% keep it the same as in etc/emqx_license.conf License = - "MjIwMTExCjAKMTAKRXZhbHVhdGlvbgpjb250YWN0QGVtcXguaW8KMjAyMjAxMDEKMzY1MDAKMTAK." - "MEUCIFc9EUjqB3SjpRqWjqmAzI4Tg4LwhCRet9scEoxMRt8fAiEAk6vfYUiPOTzBC+3EjNF3WmLTiA3B0TN5ZNwuTKbTXJQ=", + "MjIwMTExCjAKMTAKRXZhbHVhdGlvbgpjb250YWN0QGVtcXguaW8KZGVmYXVsdAoyMDIyMDQxOQoxODI1CjEwMDAK." + "MEQCICbgRVijCQov2hrvZXR1mk9Oa+tyV1F5oJ6iOZeSHjnQAiB9dUiVeaZekDOjztk+NCWjhk4PG8tWfw2uFZWruSzD6g==", ok = file:write_file(?DEFAULT_LICENSE_FILE, License), ?DEFAULT_LICENSE_FILE.