feat(license): allow setting 'default' license key
This commit is contained in:
parent
caaf8113fc
commit
14077ec43b
|
@ -59,6 +59,12 @@
|
||||||
max_connections/1
|
max_connections/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
%% for testing purpose
|
||||||
|
-export([
|
||||||
|
default/0,
|
||||||
|
pubkey/0
|
||||||
|
]).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Behaviour
|
%% Behaviour
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -82,19 +88,18 @@
|
||||||
%% API
|
%% API
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-ifdef(TEST).
|
|
||||||
pubkey() -> persistent_term:get(emqx_license_test_pubkey, ?PUBKEY).
|
|
||||||
-else.
|
|
||||||
pubkey() -> ?PUBKEY.
|
pubkey() -> ?PUBKEY.
|
||||||
-endif.
|
default() -> emqx_license_schema:default_license().
|
||||||
|
|
||||||
%% @doc Parse license key.
|
%% @doc Parse license key.
|
||||||
%% If the license key is prefixed with "file://path/to/license/file",
|
%% If the license key is prefixed with "file://path/to/license/file",
|
||||||
%% then the license key is read from the file.
|
%% then the license key is read from the file.
|
||||||
-spec parse(string() | binary()) -> {ok, license()} | {error, map()}.
|
-spec parse(default | string() | binary()) -> {ok, license()} | {error, map()}.
|
||||||
parse(Content) ->
|
parse(Content) ->
|
||||||
parse(iolist_to_binary(Content), pubkey()).
|
parse(to_bin(Content), ?MODULE:pubkey()).
|
||||||
|
|
||||||
|
parse(<<"default">>, PubKey) ->
|
||||||
|
parse(?MODULE:default(), PubKey);
|
||||||
parse(<<"file://", Path/binary>> = FileKey, PubKey) ->
|
parse(<<"file://", Path/binary>> = FileKey, PubKey) ->
|
||||||
case file:read_file(Path) of
|
case file:read_file(Path) of
|
||||||
{ok, Content} ->
|
{ok, Content} ->
|
||||||
|
@ -159,3 +164,8 @@ do_parse(Content, Key, [Module | Modules], Errors) ->
|
||||||
#{module => Module, error => Error, stacktrace => Stacktrace} | Errors
|
#{module => Module, error => Error, stacktrace => Stacktrace} | Errors
|
||||||
])
|
])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
to_bin(A) when is_atom(A) ->
|
||||||
|
atom_to_binary(A);
|
||||||
|
to_bin(L) ->
|
||||||
|
iolist_to_binary(L).
|
||||||
|
|
|
@ -38,8 +38,8 @@ tags() ->
|
||||||
fields(key_license) ->
|
fields(key_license) ->
|
||||||
[
|
[
|
||||||
{key, #{
|
{key, #{
|
||||||
type => binary(),
|
type => hoconsc:union([default, binary()]),
|
||||||
default => default_license(),
|
default => <<"default">>,
|
||||||
%% so it's not logged
|
%% so it's not logged
|
||||||
sensitive => true,
|
sensitive => true,
|
||||||
required => true,
|
required => true,
|
||||||
|
|
|
@ -16,12 +16,14 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
|
emqx_license_test_lib:mock_parser(),
|
||||||
_ = application:load(emqx_conf),
|
_ = application:load(emqx_conf),
|
||||||
emqx_config:save_schema_mod_and_names(emqx_license_schema),
|
emqx_config:save_schema_mod_and_names(emqx_license_schema),
|
||||||
emqx_common_test_helpers:start_apps([emqx_license], fun set_special_configs/1),
|
emqx_common_test_helpers:start_apps([emqx_license], fun set_special_configs/1),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
|
emqx_license_test_lib:unmock_parser(),
|
||||||
emqx_common_test_helpers:stop_apps([emqx_license]),
|
emqx_common_test_helpers:stop_apps([emqx_license]),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
@ -103,17 +105,7 @@ setup_test(TestCase, Config) when
|
||||||
),
|
),
|
||||||
ok;
|
ok;
|
||||||
(emqx_license) ->
|
(emqx_license) ->
|
||||||
LicensePath = filename:join(emqx_license:license_dir(), "emqx.lic"),
|
set_special_configs(emqx_license),
|
||||||
filelib:ensure_dir(LicensePath),
|
|
||||||
ok = file:write_file(LicensePath, LicenseKey),
|
|
||||||
LicConfig = #{type => file, file => LicensePath},
|
|
||||||
emqx_config:put([license], LicConfig),
|
|
||||||
RawConfig = #{<<"type">> => file, <<"file">> => LicensePath},
|
|
||||||
emqx_config:put_raw([<<"license">>], RawConfig),
|
|
||||||
ok = persistent_term:put(
|
|
||||||
emqx_license_test_pubkey,
|
|
||||||
emqx_license_test_lib:public_key_pem()
|
|
||||||
),
|
|
||||||
ok;
|
ok;
|
||||||
(_) ->
|
(_) ->
|
||||||
ok
|
ok
|
||||||
|
@ -129,9 +121,9 @@ teardown_test(_TestCase, _Config) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
set_special_configs(emqx_license) ->
|
set_special_configs(emqx_license) ->
|
||||||
Config = #{key => emqx_license_test_lib:default_license()},
|
Config = #{key => default},
|
||||||
emqx_config:put([license], Config),
|
emqx_config:put([license], Config),
|
||||||
RawConfig = #{<<"key">> => emqx_license_test_lib:default_license()},
|
RawConfig = #{<<"key">> => <<"default">>},
|
||||||
emqx_config:put_raw([<<"license">>], RawConfig);
|
emqx_config:put_raw([<<"license">>], RawConfig);
|
||||||
set_special_configs(_) ->
|
set_special_configs(_) ->
|
||||||
ok.
|
ok.
|
||||||
|
@ -150,7 +142,7 @@ t_update_value(_Config) ->
|
||||||
emqx_license:update_key("invalid.license")
|
emqx_license:update_key("invalid.license")
|
||||||
),
|
),
|
||||||
|
|
||||||
LicenseValue = emqx_license_test_lib:default_license(),
|
LicenseValue = emqx_license_test_lib:default_test_license(),
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, #{}},
|
{ok, #{}},
|
||||||
|
|
|
@ -16,15 +16,12 @@ all() ->
|
||||||
|
|
||||||
init_per_suite(CtConfig) ->
|
init_per_suite(CtConfig) ->
|
||||||
_ = application:load(emqx_conf),
|
_ = application:load(emqx_conf),
|
||||||
ok = persistent_term:put(
|
emqx_license_test_lib:mock_parser(),
|
||||||
emqx_license_test_pubkey,
|
|
||||||
emqx_license_test_lib:public_key_pem()
|
|
||||||
),
|
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_license], fun set_special_configs/1),
|
ok = emqx_common_test_helpers:start_apps([emqx_license], fun set_special_configs/1),
|
||||||
CtConfig.
|
CtConfig.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
persistent_term:erase(emqx_license_test_pubkey),
|
emqx_license_test_lib:unmock_parser(),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_license]).
|
ok = emqx_common_test_helpers:stop_apps([emqx_license]).
|
||||||
|
|
||||||
init_per_testcase(t_default_limits, Config) ->
|
init_per_testcase(t_default_limits, Config) ->
|
||||||
|
|
|
@ -24,15 +24,12 @@ end_per_suite(_) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_Case, Config) ->
|
init_per_testcase(_Case, Config) ->
|
||||||
ok = persistent_term:put(
|
emqx_license_test_lib:mock_parser(),
|
||||||
emqx_license_test_pubkey,
|
|
||||||
emqx_license_test_lib:public_key_pem()
|
|
||||||
),
|
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_testcase(_Case, _Config) ->
|
end_per_testcase(_Case, _Config) ->
|
||||||
persistent_term:erase(emqx_license_test_pubkey),
|
emqx_license_test_lib:unmock_parser(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
set_special_configs(emqx_license) ->
|
set_special_configs(emqx_license) ->
|
||||||
|
|
|
@ -19,6 +19,7 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
|
emqx_license_test_lib:mock_parser(),
|
||||||
_ = application:load(emqx_conf),
|
_ = application:load(emqx_conf),
|
||||||
emqx_config:save_schema_mod_and_names(emqx_license_schema),
|
emqx_config:save_schema_mod_and_names(emqx_license_schema),
|
||||||
emqx_common_test_helpers:start_apps([emqx_license, emqx_dashboard], fun set_special_configs/1),
|
emqx_common_test_helpers:start_apps([emqx_license, emqx_dashboard], fun set_special_configs/1),
|
||||||
|
@ -31,7 +32,7 @@ end_per_suite(_) ->
|
||||||
emqx_config:put([license], Config),
|
emqx_config:put([license], Config),
|
||||||
RawConfig = #{<<"key">> => LicenseKey},
|
RawConfig = #{<<"key">> => LicenseKey},
|
||||||
emqx_config:put_raw([<<"license">>], RawConfig),
|
emqx_config:put_raw([<<"license">>], RawConfig),
|
||||||
persistent_term:erase(emqx_license_test_pubkey),
|
emqx_license_test_lib:unmock_parser(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
set_special_configs(emqx_dashboard) ->
|
set_special_configs(emqx_dashboard) ->
|
||||||
|
@ -48,10 +49,6 @@ set_special_configs(emqx_license) ->
|
||||||
<<"connection_high_watermark">> => <<"80%">>
|
<<"connection_high_watermark">> => <<"80%">>
|
||||||
},
|
},
|
||||||
emqx_config:put_raw([<<"license">>], RawConfig),
|
emqx_config:put_raw([<<"license">>], RawConfig),
|
||||||
ok = persistent_term:put(
|
|
||||||
emqx_license_test_pubkey,
|
|
||||||
emqx_license_test_lib:public_key_pem()
|
|
||||||
),
|
|
||||||
ok;
|
ok;
|
||||||
set_special_configs(_) ->
|
set_special_configs(_) ->
|
||||||
ok.
|
ok.
|
||||||
|
@ -113,6 +110,19 @@ t_license_info(_Config) ->
|
||||||
),
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
t_set_default_license(_Config) ->
|
||||||
|
NewKey = <<"default">>,
|
||||||
|
Res = request(
|
||||||
|
post,
|
||||||
|
uri(["license"]),
|
||||||
|
#{key => NewKey}
|
||||||
|
),
|
||||||
|
?assertMatch({ok, 200, _}, Res),
|
||||||
|
{ok, 200, Payload} = Res,
|
||||||
|
%% assert that it's not the string "default" returned
|
||||||
|
?assertMatch(#{<<"customer">> := _}, emqx_utils_json:decode(Payload, [return_maps])),
|
||||||
|
ok.
|
||||||
|
|
||||||
t_license_upload_key_success(_Config) ->
|
t_license_upload_key_success(_Config) ->
|
||||||
NewKey = emqx_license_test_lib:make_license(#{max_connections => "999"}),
|
NewKey = emqx_license_test_lib:make_license(#{max_connections => "999"}),
|
||||||
Res = request(
|
Res = request(
|
||||||
|
|
|
@ -70,3 +70,13 @@ default_test_license() ->
|
||||||
|
|
||||||
default_license() ->
|
default_license() ->
|
||||||
emqx_license_schema:default_license().
|
emqx_license_schema:default_license().
|
||||||
|
|
||||||
|
mock_parser() ->
|
||||||
|
meck:new(emqx_license_parser, [non_strict, passthrough, no_history, no_link]),
|
||||||
|
meck:expect(emqx_license_parser, pubkey, fun() -> public_key_pem() end),
|
||||||
|
meck:expect(emqx_license_parser, default, fun() -> default_test_license() end),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
unmock_parser() ->
|
||||||
|
meck:unload(emqx_license_parser),
|
||||||
|
ok.
|
||||||
|
|
|
@ -25,15 +25,16 @@ connection_low_watermark_field_deprecated.label:
|
||||||
"""deprecated use /license/setting instead"""
|
"""deprecated use /license/setting instead"""
|
||||||
|
|
||||||
key_field.desc:
|
key_field.desc:
|
||||||
"""This configuration parameter is designated for the license key and supports two input formats:
|
"""This configuration parameter is designated for the license key and supports below input formats:
|
||||||
|
|
||||||
- Direct Key: Enter the secret key directly as a string value.
|
- Direct Key: Enter the secret key directly as a string value.
|
||||||
- File Path: Specify the path to a file that contains the secret key. Ensure the path starts with <code>file://</code>.
|
- File Path: Specify the path to a file that contains the secret key. Ensure the path starts with <code>file://</code>.
|
||||||
|
- "default": Use string value <code>"default"</code> to apply the default trial license.
|
||||||
|
|
||||||
Note: An invalid license key or an incorrect file path may prevent EMQX from starting successfully.
|
Note: An invalid license key or an incorrect file path may prevent EMQX from starting successfully.
|
||||||
If a file path is used, EMQX attempts to reload the license key every 2 minutes.
|
If a file path is used, EMQX attempts to reload the license key from the file every 2 minutes.
|
||||||
Any failure in reloading the license key will be recorded as an error level log message,
|
Any failure in reloading the license file will be recorded as an error level log message,
|
||||||
without causing system downtime."""
|
and EMQX continues to apply the license loaded previously."""
|
||||||
|
|
||||||
key_field.label:
|
key_field.label:
|
||||||
"""License string"""
|
"""License string"""
|
||||||
|
|
Loading…
Reference in New Issue