diff --git a/apps/emqx/priv/bpapi.versions b/apps/emqx/priv/bpapi.versions index 12e70e36a..d1176e7ac 100644 --- a/apps/emqx/priv/bpapi.versions +++ b/apps/emqx/priv/bpapi.versions @@ -14,6 +14,7 @@ {emqx_gateway_cm,1}. {emqx_gateway_http,1}. {emqx_license,1}. +{emqx_license,2}. {emqx_management,1}. {emqx_management,2}. {emqx_mgmt_api_plugins,1}. diff --git a/apps/emqx/test/emqx_common_test_helpers.erl b/apps/emqx/test/emqx_common_test_helpers.erl index d0ee99d6d..b3292ded3 100644 --- a/apps/emqx/test/emqx_common_test_helpers.erl +++ b/apps/emqx/test/emqx_common_test_helpers.erl @@ -595,6 +595,7 @@ setup_node(Node, Opts) when is_map(Opts) -> EnvHandler = maps:get(env_handler, Opts, fun(_) -> ok end), ConfigureGenRpc = maps:get(configure_gen_rpc, Opts, true), LoadSchema = maps:get(load_schema, Opts, true), + SchemaMod = maps:get(schema_mod, Opts, emqx_schema), LoadApps = maps:get(load_apps, Opts, [gen_rpc, emqx, ekka, mria] ++ Apps), Env = maps:get(env, Opts, []), Conf = maps:get(conf, Opts, []), @@ -630,7 +631,7 @@ setup_node(Node, Opts) when is_map(Opts) -> %% Otherwise, configuration get's loaded and all preset env in envhandler is lost LoadSchema andalso begin - emqx_config:init_load(emqx_schema), + emqx_config:init_load(SchemaMod), application:set_env(emqx, init_config_load_done, true) end, diff --git a/bin/emqx b/bin/emqx index cac5cf655..a70c676fd 100755 --- a/bin/emqx +++ b/bin/emqx @@ -416,7 +416,7 @@ call_hocon() { ## and parsing HOCON config + environment variables is a non-trivial task CONF_KEYS=( 'node.data_dir' 'node.name' 'node.cookie' 'node.db_backend' 'cluster.proto_dist' ) if [ "$IS_ENTERPRISE" = 'yes' ]; then - CONF_KEYS+=( 'license.file' 'license.key' ) + CONF_KEYS+=( 'license.type' 'license.file' 'license.key' ) fi if [ "$IS_BOOT_COMMAND" = 'yes' ]; then diff --git a/bin/nodetool b/bin/nodetool index 773f855a0..0711f32d6 100755 --- a/bin/nodetool +++ b/bin/nodetool @@ -25,9 +25,9 @@ main(Args) -> %% forward the call to hocon_cli hocon_cli:main(Rest); ["check_license_key", Key] -> - check_license(#{key => list_to_binary(Key)}); + check_license(#{type => key, key => list_to_binary(Key)}); ["check_license_file", File] -> - check_license(#{file => list_to_binary(File)}); + check_license(#{type => file, file => list_to_binary(File)}); _ -> do(Args) end. diff --git a/lib-ee/emqx_license/etc/emqx_license.conf b/lib-ee/emqx_license/etc/emqx_license.conf index 476444ea0..b5684b740 100644 --- a/lib-ee/emqx_license/etc/emqx_license.conf +++ b/lib-ee/emqx_license/etc/emqx_license.conf @@ -1,4 +1,5 @@ license { + type = key # 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%, diff --git a/lib-ee/emqx_license/src/emqx_license.app.src b/lib-ee/emqx_license/src/emqx_license.app.src index 7d10277d8..9aba01e96 100644 --- a/lib-ee/emqx_license/src/emqx_license.app.src +++ b/lib-ee/emqx_license/src/emqx_license.app.src @@ -1,6 +1,6 @@ {application, emqx_license, [ {description, "EMQX License"}, - {vsn, "5.0.0"}, + {vsn, "5.0.1"}, {modules, []}, {registered, [emqx_license_sup]}, {applications, [kernel, stdlib]}, diff --git a/lib-ee/emqx_license/src/emqx_license.erl b/lib-ee/emqx_license/src/emqx_license.erl index 787a8b283..24b2cc709 100644 --- a/lib-ee/emqx_license/src/emqx_license.erl +++ b/lib-ee/emqx_license/src/emqx_license.erl @@ -22,7 +22,9 @@ read_license/0, read_license/1, update_file/1, - update_key/1 + update_key/1, + license_dir/0, + save_and_backup_license/1 ]). -define(CONF_KEY_PATH, [license]). @@ -54,15 +56,29 @@ unload() -> emqx_conf:remove_handler(?CONF_KEY_PATH), emqx_license_cli:unload(). +-spec license_dir() -> file:filename(). +license_dir() -> + filename:join([emqx:data_dir(), licenses]). + +%% Subdirectory relative to data dir. +-spec relative_license_path() -> file:filename(). +relative_license_path() -> + filename:join([licenses, "emqx.lic"]). + -spec update_file(binary() | string()) -> {ok, emqx_config:update_result()} | {error, emqx_config:update_error()}. 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} - ), - handle_config_update_result(Result). + case file:read_file(Filename) of + {ok, Contents} -> + Result = emqx_conf:update( + ?CONF_KEY_PATH, + {file, Contents}, + #{rawconf_with_defaults => true, override_to => local} + ), + handle_config_update_result(Result); + {error, Error} -> + {error, Error} + end. -spec update_key(binary() | string()) -> {ok, emqx_config:update_result()} | {error, emqx_config:update_error()}. @@ -125,22 +141,18 @@ del_license_hook() -> _ = emqx_hooks:del('client.connect', {?MODULE, check, []}), ok. -do_update({file, Filename}, Conf) -> - case file:read_file(Filename) of - {ok, Content} -> - case emqx_license_parser:parse(Content) of - {ok, _License} -> - maps:remove(<<"key">>, Conf#{<<"file">> => Filename}); - {error, Reason} -> - erlang:throw(Reason) - end; - {error, Reason} -> - erlang:throw({invalid_license_file, Reason}) - end; +do_update({file, NewContents}, Conf) -> + Res = emqx_license_proto_v2:save_and_backup_license(mria_mnesia:running_nodes(), NewContents), + %% assert + true = lists:all(fun(X) -> X =:= {ok, ok} end, Res), + %% Must be relative to the data dir, since different nodes might + %% have different data directories configured... + LicensePath = relative_license_path(), + maps:remove(<<"key">>, Conf#{<<"type">> => file, <<"file">> => LicensePath}); do_update({key, Content}, Conf) when is_binary(Content); is_list(Content) -> case emqx_license_parser:parse(Content) of {ok, _License} -> - maps:remove(<<"file">>, Conf#{<<"key">> => Content}); + maps:remove(<<"file">>, Conf#{<<"type">> => key, <<"key">> => Content}); {error, Reason} -> erlang:throw(Reason) end; @@ -148,17 +160,61 @@ do_update({key, Content}, Conf) when is_binary(Content); is_list(Content) -> do_update(_Other, Conf) -> Conf. +save_and_backup_license(NewLicenseKey) -> + %% Must be relative to the data dir, since different nodes might + %% have different data directories configured... + CurrentLicensePath = filename:join(emqx:data_dir(), relative_license_path()), + LicenseDir = filename:dirname(CurrentLicensePath), + case filelib:ensure_dir(CurrentLicensePath) of + ok -> ok; + {error, EnsureError} -> throw({error_creating_license_dir, EnsureError}) + end, + case file:read_file(CurrentLicensePath) of + {ok, NewLicenseKey} -> + %% same contents; nothing to do. + ok; + {ok, _OldContents} -> + Time = calendar:system_time_to_rfc3339(erlang:system_time(second)), + BackupPath = filename:join([ + LicenseDir, + "emqx.lic." ++ Time ++ ".backup" + ]), + case file:copy(CurrentLicensePath, BackupPath) of + {ok, _} -> ok; + {error, CopyError} -> throw({error_backing_up_license, CopyError}) + end, + ok; + {error, enoent} -> + ok; + {error, Error} -> + throw({error_reading_existing_license, Error}) + end, + case file:write_file(CurrentLicensePath, NewLicenseKey) of + ok -> ok; + {error, WriteError} -> throw({error_writing_license, WriteError}) + end, + ok. + check_max_clients_exceeded(MaxClients) -> emqx_license_resources:connection_count() > MaxClients * 1.1. -read_license(#{file := Filename}) -> +read_license(#{type := file, file := Filename}) -> case file:read_file(Filename) of - {ok, Content} -> emqx_license_parser:parse(Content); - {error, _} = Error -> Error + {ok, Content} -> + emqx_license_parser:parse(Content); + {error, _} = Error -> + %% Could be a relative path in data folder after update. + FilenameDataDir = filename:join(emqx:data_dir(), Filename), + case file:read_file(FilenameDataDir) of + {ok, Content} -> emqx_license_parser:parse(Content); + _Error -> Error + end end; -read_license(#{key := Content}) -> +read_license(#{type := key, key := Content}) -> emqx_license_parser:parse(Content). +handle_config_update_result({error, {post_config_update, ?MODULE, Error}}) -> + {error, Error}; handle_config_update_result({error, _} = Error) -> Error; handle_config_update_result({ok, #{post_config_update := #{emqx_license := Result}}}) -> diff --git a/lib-ee/emqx_license/src/emqx_license_resources.erl b/lib-ee/emqx_license/src/emqx_license_resources.erl index 551601923..4aaf853a3 100644 --- a/lib-ee/emqx_license/src/emqx_license_resources.erl +++ b/lib-ee/emqx_license/src/emqx_license_resources.erl @@ -128,6 +128,6 @@ ensure_timer(#{check_peer_interval := CheckInterval} = State) -> remote_connection_count() -> Nodes = mria_mnesia:running_nodes() -- [node()], - Results = emqx_license_proto_v1:remote_connection_counts(Nodes), + Results = emqx_license_proto_v2:remote_connection_counts(Nodes), Counts = [Count || {ok, Count} <- Results], lists:sum(Counts). diff --git a/lib-ee/emqx_license/src/emqx_license_schema.erl b/lib-ee/emqx_license/src/emqx_license_schema.erl index d6517ab88..88d245eb3 100644 --- a/lib-ee/emqx_license/src/emqx_license_schema.erl +++ b/lib-ee/emqx_license/src/emqx_license_schema.erl @@ -14,14 +14,15 @@ -export([roots/0, fields/1, validations/0, desc/1]). +-export([ + license_type/0 +]). + roots() -> [ {license, hoconsc:mk( - hoconsc:union([ - hoconsc:ref(?MODULE, key_license), - hoconsc:ref(?MODULE, file_license) - ]), + license_type(), #{ desc => "EMQX Enterprise license.\n" @@ -36,16 +37,35 @@ roots() -> fields(key_license) -> [ + {type, #{ + type => key, + required => true + }}, {key, #{ type => string(), %% so it's not logged sensitive => true, + required => true, desc => "License string" + }}, + {file, #{ + type => string(), + required => false }} | common_fields() ]; fields(file_license) -> [ + {type, #{ + type => file, + required => true + }}, + {key, #{ + type => string(), + %% so it's not logged + sensitive => true, + required => false + }}, {file, #{ type => string(), desc => "Path to the license file" @@ -77,6 +97,12 @@ common_fields() -> validations() -> [{check_license_watermark, fun check_license_watermark/1}]. +license_type() -> + hoconsc:union([ + hoconsc:ref(?MODULE, key_license), + hoconsc:ref(?MODULE, file_license) + ]). + check_license_watermark(Conf) -> case hocon_maps:get("license.connection_low_watermark", Conf) of undefined -> diff --git a/lib-ee/emqx_license/src/proto/emqx_license_proto_v2.erl b/lib-ee/emqx_license/src/proto/emqx_license_proto_v2.erl new file mode 100644 index 000000000..6af1cea77 --- /dev/null +++ b/lib-ee/emqx_license/src/proto/emqx_license_proto_v2.erl @@ -0,0 +1,30 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved. +%%-------------------------------------------------------------------- + +-module(emqx_license_proto_v2). + +-behaviour(emqx_bpapi). + +-include_lib("emqx/include/bpapi.hrl"). + +-export([introduced_in/0]). + +-export([ + remote_connection_counts/1, + save_and_backup_license/2 +]). + +-define(TIMEOUT, 500). +-define(BACKUP_TIMEOUT, 15_000). + +introduced_in() -> + "5.0.5". + +-spec remote_connection_counts(list(node())) -> list({atom(), term()}). +remote_connection_counts(Nodes) -> + erpc:multicall(Nodes, emqx_license_resources, local_connection_count, [], ?TIMEOUT). + +-spec save_and_backup_license(list(node()), binary()) -> list({atom(), term()}). +save_and_backup_license(Nodes, NewLicenseKey) -> + erpc:multicall(Nodes, emqx_license, save_and_backup_license, [NewLicenseKey], ?BACKUP_TIMEOUT). diff --git a/lib-ee/emqx_license/test/emqx_license_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_SUITE.erl index bc73f9071..a648595d2 100644 --- a/lib-ee/emqx_license/test/emqx_license_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_SUITE.erl @@ -28,39 +28,190 @@ end_per_suite(_) -> init_per_testcase(Case, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), set_invalid_license_file(Case), - Config. + Paths = set_override_paths(Case), + Config0 = setup_test(Case, Config), + Paths ++ Config0 ++ Config. -end_per_testcase(Case, _Config) -> +end_per_testcase(Case, Config) -> restore_valid_license_file(Case), + clean_overrides(Case, Config), + teardown_test(Case, Config), + ok. + +set_override_paths(TestCase) when + TestCase =:= t_change_from_file_to_key; + TestCase =:= t_change_from_key_to_file +-> + LocalOverridePath = filename:join([ + "/tmp", + "local-" ++ atom_to_list(TestCase) ++ ".conf" + ]), + ClusterOverridePath = filename:join([ + "/tmp", + "local-" ++ atom_to_list(TestCase) ++ ".conf" + ]), + application:set_env(emqx, local_override_conf_file, LocalOverridePath), + application:set_env(emqx, cluster_override_conf_file, ClusterOverridePath), + [ + {local_override_path, LocalOverridePath}, + {cluster_override_path, ClusterOverridePath} + ]; +set_override_paths(_TestCase) -> + []. + +clean_overrides(TestCase, Config) when + TestCase =:= t_change_from_file_to_key; + TestCase =:= t_change_from_key_to_file +-> + LocalOverridePath = ?config(local_override_path, Config), + ClusterOverridePath = ?config(cluster_override_path, Config), + file:delete(LocalOverridePath), + file:delete(ClusterOverridePath), + application:unset_env(emqx, local_override_conf_file), + application:unset_env(emqx, cluster_override_conf_file), + ok; +clean_overrides(_TestCase, _Config) -> + ok. + +setup_test(TestCase, Config) when + TestCase =:= t_update_file_cluster_backup +-> + DataDir = ?config(data_dir, Config), + {LicenseKey, _License} = mk_license( + [ + %% license format version + "220111", + %% license type + "0", + %% customer type + "10", + %% customer name + "Foo", + %% customer email + "contact@foo.com", + %% deplayment name + "bar-deployment", + %% start date + "20220111", + %% days + "100000", + %% max connections + "19" + ] + ), + Cluster = emqx_common_test_helpers:emqx_cluster( + [core, core], + [ + {apps, [emqx_conf, emqx_license]}, + {load_schema, false}, + {schema_mod, emqx_enterprise_conf_schema}, + {env_handler, fun + (emqx) -> + emqx_config:save_schema_mod_and_names(emqx_enterprise_conf_schema), + %% emqx_config:save_schema_mod_and_names(emqx_license_schema), + application:set_env(emqx, boot_modules, []), + application:set_env( + emqx, + data_dir, + filename:join([ + DataDir, + TestCase, + node() + ]) + ), + ok; + (emqx_conf) -> + emqx_config:save_schema_mod_and_names(emqx_enterprise_conf_schema), + %% emqx_config:save_schema_mod_and_names(emqx_license_schema), + application:set_env( + emqx, + data_dir, + filename:join([ + DataDir, + TestCase, + node() + ]) + ), + ok; + (emqx_license) -> + LicensePath = filename:join(emqx_license:license_dir(), "emqx.lic"), + 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 = meck:new(emqx_license, [non_strict, passthrough, no_history, no_link]), + %% meck:expect(emqx_license, read_license, fun() -> {ok, License} end), + meck:expect( + emqx_license_parser, + parse, + fun(X) -> + emqx_license_parser:parse( + X, + emqx_license_test_lib:public_key_pem() + ) + end + ), + ok; + (_) -> + ok + end} + ] + ), + Nodes = [emqx_common_test_helpers:start_slave(Name, Opts) || {Name, Opts} <- Cluster], + [{nodes, Nodes}, {cluster, Cluster}, {old_license, LicenseKey}]; +setup_test(_TestCase, _Config) -> + []. + +teardown_test(TestCase, Config) when + TestCase =:= t_update_file_cluster_backup +-> + Nodes = ?config(nodes, Config), + lists:foreach( + fun(N) -> + LicenseDir = erpc:call(N, emqx_license, license_dir, []), + {ok, _} = emqx_common_test_helpers:stop_slave(N), + ok = file:del_dir_r(LicenseDir), + ok + end, + Nodes + ), + ok; +teardown_test(_TestCase, _Config) -> ok. set_invalid_license_file(t_read_license_from_invalid_file) -> - Config = #{file => "/invalid/file"}, + Config = #{type => file, file => "/invalid/file"}, emqx_config:put([license], Config); set_invalid_license_file(_) -> ok. restore_valid_license_file(t_read_license_from_invalid_file) -> - Config = #{file => emqx_license_test_lib:default_license()}, + Config = #{type => file, file => emqx_license_test_lib:default_license()}, emqx_config:put([license], Config); restore_valid_license_file(_) -> ok. set_special_configs(emqx_license) -> - Config = #{file => emqx_license_test_lib:default_license()}, + Config = #{type => file, file => emqx_license_test_lib:default_license()}, emqx_config:put([license], Config), - RawConfig = #{<<"file">> => emqx_license_test_lib:default_license()}, + RawConfig = #{<<"type">> => file, <<"file">> => emqx_license_test_lib:default_license()}, emqx_config:put_raw([<<"license">>], RawConfig); set_special_configs(_) -> ok. +assert_on_nodes(Nodes, RunFun, CheckFun) -> + Res = [{N, erpc:call(N, RunFun)} || N <- Nodes], + lists:foreach(CheckFun, Res). + %%------------------------------------------------------------------------------ %% Tests %%------------------------------------------------------------------------------ t_update_file(_Config) -> ?assertMatch( - {error, {invalid_license_file, enoent}}, + {error, enoent}, emqx_license:update_file("/unknown/path") ), @@ -75,6 +226,115 @@ t_update_file(_Config) -> emqx_license:update_file(emqx_license_test_lib:default_license()) ). +t_update_file_cluster_backup(Config) -> + OldLicenseKey = ?config(old_license, Config), + Nodes = [N1 | _] = ?config(nodes, Config), + + %% update the license file for the cluster + {NewLicenseKey, NewDecodedLicense} = mk_license( + [ + %% license format version + "220111", + %% license type + "0", + %% customer type + "10", + %% customer name + "Foo", + %% customer email + "contact@foo.com", + %% deplayment name + "bar-deployment", + %% start date + "20220111", + %% days + "100000", + %% max connections + "190" + ] + ), + NewLicensePath = "tmp_new_license.lic", + ok = file:write_file(NewLicensePath, NewLicenseKey), + {ok, _} = erpc:call(N1, emqx_license, update_file, [NewLicensePath]), + + assert_on_nodes( + Nodes, + fun() -> + Conf = emqx_conf:get([license]), + emqx_license:read_license(Conf) + end, + fun({N, Res}) -> + ?assertMatch({ok, _}, Res, #{node => N}), + {ok, License} = Res, + ?assertEqual(NewDecodedLicense, License, #{node => N}) + end + ), + + assert_on_nodes( + Nodes, + fun() -> + LicenseDir = emqx_license:license_dir(), + file:list_dir(LicenseDir) + end, + fun({N, Res}) -> + ?assertMatch({ok, _}, Res, #{node => N}), + {ok, DirContents} = Res, + %% the now current license + ?assert(lists:member("emqx.lic", DirContents), #{node => N, dir_contents => DirContents}), + %% the backed up old license + ?assert( + lists:any( + fun + ("emqx.lic." ++ Suffix) -> lists:suffix(".backup", Suffix); + (_) -> false + end, + DirContents + ), + #{node => N, dir_contents => DirContents} + ) + end + ), + + assert_on_nodes( + Nodes, + fun() -> + LicenseDir = emqx_license:license_dir(), + {ok, DirContents} = file:list_dir(LicenseDir), + [BackupLicensePath0] = [ + F + || "emqx.lic." ++ F <- DirContents, lists:suffix(".backup", F) + ], + BackupLicensePath = "emqx.lic." ++ BackupLicensePath0, + {ok, BackupLicense} = file:read_file(filename:join(LicenseDir, BackupLicensePath)), + {ok, NewLicense} = file:read_file(filename:join(LicenseDir, "emqx.lic")), + #{ + backup => BackupLicense, + new => NewLicense + } + end, + fun({N, #{backup := BackupLicense, new := NewLicense}}) -> + ?assertEqual(OldLicenseKey, BackupLicense, #{node => N}), + ?assertEqual(NewLicenseKey, NewLicense, #{node => N}) + end + ), + + %% uploading the same license twice should not generate extra backups. + {ok, _} = erpc:call(N1, emqx_license, update_file, [NewLicensePath]), + + assert_on_nodes( + Nodes, + fun() -> + LicenseDir = emqx_license:license_dir(), + {ok, DirContents} = file:list_dir(LicenseDir), + [F || "emqx.lic." ++ F <- DirContents, lists:suffix(".backup", F)] + end, + fun({N, Backups}) -> + ?assertMatch([_], Backups, #{node => N}) + end + ), + + ok. + t_update_value(_Config) -> ?assertMatch( {error, [_ | _]}, @@ -95,7 +355,7 @@ t_read_license_from_invalid_file(_Config) -> ). t_check_exceeded(_Config) -> - License = mk_license( + {_, License} = mk_license( [ "220111", "0", @@ -124,7 +384,7 @@ t_check_exceeded(_Config) -> ). t_check_ok(_Config) -> - License = mk_license( + {_, License} = mk_license( [ "220111", "0", @@ -153,7 +413,7 @@ t_check_ok(_Config) -> ). t_check_expired(_Config) -> - License = mk_license( + {_, License} = mk_license( [ "220111", %% Official customer @@ -183,6 +443,39 @@ t_check_not_loaded(_Config) -> emqx_license:check(#{}, #{}) ). +t_change_from_file_to_key(_Config) -> + %% precondition + ?assertMatch(#{file := _}, emqx_conf:get([license])), + + OldConf = emqx_conf:get_raw([]), + + %% this saves updated config to `{cluster,local}-overrrides.conf' + {ok, LicenseValue} = file:read_file(emqx_license_test_lib:default_license()), + {ok, _NewConf} = emqx_license:update_key(LicenseValue), + + %% assert that `{cluster,local}-overrides.conf' merge correctly + ?assertEqual(ok, emqx_config:init_load(emqx_license_schema, OldConf, #{})), + + ok. + +t_change_from_key_to_file(_Config) -> + Config = #{type => key, key => <<"some key">>}, + emqx_config:put([license], Config), + RawConfig = #{<<"type">> => key, <<"key">> => <<"some key">>}, + emqx_config:put_raw([<<"license">>], RawConfig), + + %% precondition + ?assertMatch(#{type := key, key := _}, emqx_conf:get([license])), + OldConf = emqx_conf:get_raw([]), + + %% this saves updated config to `{cluster,local}-overrrides.conf' + {ok, _NewConf} = emqx_license:update_file(emqx_license_test_lib:default_license()), + + %% assert that `{cluster,local}-overrides.conf' merge correctly + ?assertEqual(ok, emqx_config:init_load(emqx_license_schema, OldConf, #{})), + + ok. + %%------------------------------------------------------------------------------ %% Helpers %%------------------------------------------------------------------------------ @@ -193,4 +486,4 @@ mk_license(Fields) -> EncodedLicense, emqx_license_test_lib:public_key_pem() ), - License. + {EncodedLicense, License}. 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 8842db7f9..0e10b684d 100644 --- a/lib-ee/emqx_license/test/emqx_license_checker_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_checker_SUITE.erl @@ -35,7 +35,7 @@ end_per_testcase(_Case, _Config) -> ok. set_special_configs(emqx_license) -> - Config = #{file => emqx_license_test_lib:default_license()}, + Config = #{type => file, file => emqx_license_test_lib:default_license()}, emqx_config:put([license], Config); set_special_configs(_) -> ok. diff --git a/lib-ee/emqx_license/test/emqx_license_cli_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_cli_SUITE.erl index ab7fd2dc8..5cf11adda 100644 --- a/lib-ee/emqx_license/test/emqx_license_cli_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_cli_SUITE.erl @@ -31,9 +31,9 @@ end_per_testcase(_Case, _Config) -> ok. set_special_configs(emqx_license) -> - Config = #{file => emqx_license_test_lib:default_license()}, + Config = #{type => file, file => emqx_license_test_lib:default_license()}, emqx_config:put([license], Config), - RawConfig = #{<<"file">> => emqx_license_test_lib:default_license()}, + RawConfig = #{<<"type">> => file, <<"file">> => emqx_license_test_lib:default_license()}, emqx_config:put_raw([<<"license">>], RawConfig); set_special_configs(_) -> ok. diff --git a/lib-ee/emqx_license/test/emqx_license_installer_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_installer_SUITE.erl index e92c082be..e62c4d814 100644 --- a/lib-ee/emqx_license/test/emqx_license_installer_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_installer_SUITE.erl @@ -31,7 +31,7 @@ end_per_testcase(_Case, _Config) -> ok. set_special_configs(emqx_license) -> - Config = #{file => emqx_license_test_lib:default_license()}, + Config = #{type => file, file => emqx_license_test_lib:default_license()}, emqx_config:put([license], Config); set_special_configs(_) -> ok. 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 3b4e78a49..e9868cdc1 100644 --- a/lib-ee/emqx_license/test/emqx_license_parser_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_parser_SUITE.erl @@ -30,7 +30,7 @@ end_per_testcase(_Case, _Config) -> ok. set_special_configs(emqx_license) -> - Config = #{file => emqx_license_test_lib:default_license()}, + Config = #{type => file, file => emqx_license_test_lib:default_license()}, emqx_config:put([license], Config); set_special_configs(_) -> ok. 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 b1c45ade7..61f3c4cd8 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 @@ -30,7 +30,7 @@ end_per_testcase(_Case, _Config) -> ok. set_special_configs(emqx_license) -> - Config = #{file => emqx_license_test_lib:default_license()}, + Config = #{type => file, file => emqx_license_test_lib:default_license()}, emqx_config:put([license], Config); set_special_configs(_) -> ok. diff --git a/lib-ee/emqx_license/test/emqx_license_resources_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_resources_SUITE.erl index a6411902e..84a63e611 100644 --- a/lib-ee/emqx_license/test/emqx_license_resources_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_resources_SUITE.erl @@ -31,7 +31,7 @@ end_per_testcase(_Case, _Config) -> ok. set_special_configs(emqx_license) -> - Config = #{file => emqx_license_test_lib:default_license()}, + Config = #{type => file, file => emqx_license_test_lib:default_license()}, emqx_config:put([license], Config); set_special_configs(_) -> ok. @@ -59,9 +59,9 @@ t_connection_count(_Config) -> meck:new(emqx_cm, [passthrough]), meck:expect(emqx_cm, get_connected_client_count, fun() -> 10 end), - meck:new(emqx_license_proto_v1, [passthrough]), + meck:new(emqx_license_proto_v2, [passthrough]), meck:expect( - emqx_license_proto_v1, + emqx_license_proto_v2, remote_connection_counts, fun(_Nodes) -> [{ok, 5}, {error, some_error}] @@ -82,8 +82,8 @@ t_connection_count(_Config) -> end ), - meck:unload(emqx_license_proto_v1), + meck:unload(emqx_license_proto_v2), meck:unload(emqx_cm). t_emqx_license_proto(_Config) -> - ?assert("5.0.0" =< emqx_license_proto_v1:introduced_in()). + ?assert("5.0.0" =< emqx_license_proto_v2:introduced_in()).