diff --git a/apps/emqx_enterprise/src/emqx_enterprise.app.src b/apps/emqx_enterprise/src/emqx_enterprise.app.src index 37c74bdec..ac35da5b9 100644 --- a/apps/emqx_enterprise/src/emqx_enterprise.app.src +++ b/apps/emqx_enterprise/src/emqx_enterprise.app.src @@ -1,6 +1,6 @@ {application, emqx_enterprise, [ {description, "EMQX Enterprise Edition"}, - {vsn, "0.1.1"}, + {vsn, "0.1.2"}, {registered, []}, {applications, [ kernel, diff --git a/apps/emqx_enterprise/src/emqx_enterprise_schema.erl b/apps/emqx_enterprise/src/emqx_enterprise_schema.erl index 67e8a9c9a..5da65e2f5 100644 --- a/apps/emqx_enterprise/src/emqx_enterprise_schema.erl +++ b/apps/emqx_enterprise/src/emqx_enterprise_schema.erl @@ -35,7 +35,7 @@ desc(Name) -> ee_delegate(desc, ?EE_SCHEMA_MODULES, Name). validations() -> - emqx_conf_schema:validations(). + emqx_conf_schema:validations() ++ emqx_license_schema:validations(). %%------------------------------------------------------------------------------ %% helpers diff --git a/changes/ce/feat-11249.en.md b/changes/ce/feat-11249.en.md new file mode 100644 index 000000000..fcbe03716 --- /dev/null +++ b/changes/ce/feat-11249.en.md @@ -0,0 +1 @@ +Support HTTP API for setting alarm watermark of license. diff --git a/lib-ee/emqx_license/src/emqx_license.erl b/lib-ee/emqx_license/src/emqx_license.erl index 0918293a6..eaa9661e3 100644 --- a/lib-ee/emqx_license/src/emqx_license.erl +++ b/lib-ee/emqx_license/src/emqx_license.erl @@ -22,7 +22,8 @@ unload/0, read_license/0, read_license/1, - update_key/1 + update_key/1, + update_setting/1 ]). -define(CONF_KEY_PATH, [license]). @@ -64,6 +65,14 @@ update_key(Value) when is_binary(Value); is_list(Value) -> ), handle_config_update_result(Result). +update_setting(Setting) when is_map(Setting) -> + Result = emqx_conf:update( + ?CONF_KEY_PATH, + {setting, Setting}, + #{rawconf_with_defaults => true, override_to => cluster} + ), + handle_config_update_result(Result). + %%------------------------------------------------------------------------------ %% emqx_hooks %%------------------------------------------------------------------------------ @@ -96,6 +105,8 @@ check(_ConnInfo, AckProps) -> pre_config_update(_, Cmd, Conf) -> {ok, do_update(Cmd, Conf)}. +post_config_update(_Path, {setting, _}, NewConf, _Old, _AppEnvs) -> + {ok, NewConf}; post_config_update(_Path, _Cmd, NewConf, _Old, _AppEnvs) -> case read_license(NewConf) of {ok, License} -> @@ -122,6 +133,8 @@ do_update({key, Content}, Conf) when is_binary(Content); is_list(Content) -> {error, Reason} -> erlang:throw(Reason) end; +do_update({setting, Setting}, Conf) -> + maps:merge(Conf, Setting); do_update(NewConf, _PrevConf) -> #{<<"key">> := NewKey} = NewConf, do_update({key, NewKey}, NewConf). diff --git a/lib-ee/emqx_license/src/emqx_license_http_api.erl b/lib-ee/emqx_license/src/emqx_license_http_api.erl index ef0e1ee52..8f563300b 100644 --- a/lib-ee/emqx_license/src/emqx_license_http_api.erl +++ b/lib-ee/emqx_license/src/emqx_license_http_api.erl @@ -13,11 +13,14 @@ namespace/0, api_spec/0, paths/0, - schema/1 + schema/1, + fields/1 ]). +-define(LICENSE_TAGS, [<<"License">>]). -export([ - '/license'/2 + '/license'/2, + '/license/setting'/2 ]). -define(BAD_REQUEST, 'BAD_REQUEST'). @@ -29,14 +32,15 @@ api_spec() -> paths() -> [ - "/license" + "/license", + "/license/setting" ]. schema("/license") -> #{ 'operationId' => '/license', get => #{ - tags => [<<"license">>], + tags => ?LICENSE_TAGS, summary => <<"Get license info">>, description => ?DESC("desc_license_info_api"), responses => #{ @@ -50,19 +54,18 @@ schema("/license") -> ) } }, + %% TODO(5.x): It's a update action, should use PUT instead post => #{ - tags => [<<"license">>], + tags => ?LICENSE_TAGS, summary => <<"Update license key">>, description => ?DESC("desc_license_key_api"), 'requestBody' => emqx_dashboard_swagger:schema_with_examples( - emqx_license_schema:key_license(), + hoconsc:ref(?MODULE, key_license), #{ license_key => #{ summary => <<"License key string">>, value => #{ - <<"key">> => <<"xxx">>, - <<"connection_low_watermark">> => "75%", - <<"connection_high_watermark">> => "80%" + <<"key">> => <<"xxx">> } } } @@ -79,6 +82,28 @@ schema("/license") -> 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad license key">>) } } + }; +schema("/license/setting") -> + #{ + 'operationId' => '/license/setting', + get => #{ + tags => ?LICENSE_TAGS, + summary => <<"Get license setting">>, + description => ?DESC("desc_license_setting_api"), + responses => #{ + 200 => setting() + } + }, + put => #{ + tags => ?LICENSE_TAGS, + summary => <<"Update license setting">>, + description => ?DESC("desc_license_setting_api"), + 'requestBody' => setting(), + responses => #{ + 200 => setting(), + 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad setting value">>) + } + } }. sample_license_info_response() -> @@ -117,3 +142,24 @@ error_msg(Code, Msg) -> end; '/license'(post, _Params) -> {400, error_msg(?BAD_REQUEST, <<"Invalid request params">>)}. + +'/license/setting'(get, _Params) -> + {200, maps:remove(<<"key">>, emqx_config:get_raw([license]))}; +'/license/setting'(put, #{body := Setting}) -> + case emqx_license:update_setting(Setting) of + {error, Error} -> + ?SLOG(error, #{ + msg => "bad_license_setting", + reason => Error + }), + {400, error_msg(?BAD_REQUEST, <<"Bad license setting">>)}; + {ok, _} -> + ?SLOG(info, #{msg => "updated_license_setting"}), + '/license/setting'(get, undefined) + end. + +fields(key_license) -> + [lists:keyfind(key, 1, emqx_license_schema:fields(key_license))]. + +setting() -> + lists:keydelete(key, 1, emqx_license_schema:fields(key_license)). diff --git a/lib-ee/emqx_license/src/emqx_license_schema.erl b/lib-ee/emqx_license/src/emqx_license_schema.erl index bb7868d8d..8f2d7f20d 100644 --- a/lib-ee/emqx_license/src/emqx_license_schema.erl +++ b/lib-ee/emqx_license/src/emqx_license_schema.erl @@ -16,15 +16,14 @@ -export([roots/0, fields/1, validations/0, desc/1, tags/0]). -export([ - default_license/0, - key_license/0 + default_license/0 ]). roots() -> [ {license, hoconsc:mk( - key_license(), + hoconsc:ref(?MODULE, key_license), #{ desc => ?DESC(license_root) } @@ -47,11 +46,13 @@ fields(key_license) -> {connection_low_watermark, #{ type => emqx_schema:percent(), default => <<"75%">>, + example => <<"75%">>, desc => ?DESC(connection_low_watermark_field) }}, {connection_high_watermark, #{ type => emqx_schema:percent(), default => <<"80%">>, + example => <<"80%">>, desc => ?DESC(connection_high_watermark_field) }} ]. @@ -64,9 +65,6 @@ desc(_) -> validations() -> [{check_license_watermark, fun check_license_watermark/1}]. -key_license() -> - hoconsc:ref(?MODULE, key_license). - check_license_watermark(Conf) -> case hocon_maps:get("license.connection_low_watermark", Conf) of undefined -> diff --git a/lib-ee/emqx_license/test/emqx_license_http_api_SUITE.erl b/lib-ee/emqx_license/test/emqx_license_http_api_SUITE.erl index 3de5ae121..4ee0c8c8e 100644 --- a/lib-ee/emqx_license/test/emqx_license_http_api_SUITE.erl +++ b/lib-ee/emqx_license/test/emqx_license_http_api_SUITE.erl @@ -38,9 +38,15 @@ set_special_configs(emqx_dashboard) -> emqx_dashboard_api_test_helpers:set_default_config(<<"license_admin">>); set_special_configs(emqx_license) -> LicenseKey = emqx_license_test_lib:make_license(#{max_connections => "100"}), - Config = #{key => LicenseKey}, + Config = #{ + key => LicenseKey, connection_low_watermark => 0.75, connection_high_watermark => 0.8 + }, emqx_config:put([license], Config), - RawConfig = #{<<"key">> => LicenseKey}, + RawConfig = #{ + <<"key">> => LicenseKey, + <<"connection_low_watermark">> => <<"75%">>, + <<"connection_high_watermark">> => <<"80%">> + }, emqx_config:put_raw([<<"license">>], RawConfig), ok = persistent_term:put( emqx_license_test_pubkey, @@ -172,3 +178,46 @@ t_license_upload_key_not_json(_Config) -> ), assert_untouched_license(), ok. + +t_license_setting(_Config) -> + %% get + GetRes = request(get, uri(["license", "setting"]), []), + validate_setting(GetRes, <<"75%">>, <<"80%">>), + %% update + Low = <<"50%">>, + High = <<"55%">>, + UpdateRes = request(put, uri(["license", "setting"]), #{ + <<"connection_low_watermark">> => Low, + <<"connection_high_watermark">> => High + }), + validate_setting(UpdateRes, Low, High), + ?assertEqual(0.5, emqx_config:get([license, connection_low_watermark])), + ?assertEqual(0.55, emqx_config:get([license, connection_high_watermark])), + + %% update bad setting low >= high + ?assertMatch( + {ok, 400, _}, + request(put, uri(["license", "setting"]), #{ + <<"connection_low_watermark">> => <<"50%">>, + <<"connection_high_watermark">> => <<"50%">> + }) + ), + ?assertMatch( + {ok, 400, _}, + request(put, uri(["license", "setting"]), #{ + <<"connection_low_watermark">> => <<"51%">>, + <<"connection_high_watermark">> => <<"50%">> + }) + ), + ok. + +validate_setting(Res, ExpectLow, ExpectHigh) -> + ?assertMatch({ok, 200, _}, Res), + {ok, 200, Payload} = Res, + ?assertEqual( + #{ + <<"connection_low_watermark">> => ExpectLow, + <<"connection_high_watermark">> => ExpectHigh + }, + emqx_utils_json:decode(Payload, [return_maps]) + ). diff --git a/mix.exs b/mix.exs index 65c942394..41de27139 100644 --- a/mix.exs +++ b/mix.exs @@ -189,7 +189,8 @@ defmodule EMQXUmbrella.MixProject do :emqx_bridge_rabbitmq, :emqx_bridge_clickhouse, :emqx_ft, - :emqx_s3 + :emqx_s3, + :emqx_enterprise ]) end diff --git a/rebar.config.erl b/rebar.config.erl index 5f86afaa2..5db22990a 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -102,6 +102,7 @@ is_community_umbrella_app("apps/emqx_oracle") -> false; is_community_umbrella_app("apps/emqx_bridge_rabbitmq") -> false; is_community_umbrella_app("apps/emqx_ft") -> false; is_community_umbrella_app("apps/emqx_s3") -> false; +is_community_umbrella_app("apps/emqx_enterprise") -> false; is_community_umbrella_app(_) -> true. is_jq_supported() -> diff --git a/rel/i18n/emqx_license_http_api.hocon b/rel/i18n/emqx_license_http_api.hocon index 895041c18..63c9dc558 100644 --- a/rel/i18n/emqx_license_http_api.hocon +++ b/rel/i18n/emqx_license_http_api.hocon @@ -12,4 +12,10 @@ desc_license_key_api.desc: desc_license_key_api.label: """Update license""" +desc_license_setting_api.desc: +"""Update license setting""" + +desc_license_setting_api.label: +"""Update license setting""" + } diff --git a/rel/i18n/emqx_license_schema.hocon b/rel/i18n/emqx_license_schema.hocon index 3e4e37bff..51387ed39 100644 --- a/rel/i18n/emqx_license_schema.hocon +++ b/rel/i18n/emqx_license_schema.hocon @@ -12,6 +12,18 @@ connection_low_watermark_field.desc: connection_low_watermark_field.label: """Connection low watermark""" +connection_high_watermark_field_deprecated.desc: +"""deprecated use /license/setting instead""" + +connection_high_watermark_field_deprecated.label: +"""deprecated use /license/setting instead""" + +connection_low_watermark_field_deprecated.desc: +"""deprecated use /license/setting instead""" + +connection_low_watermark_field_deprecated.label: +"""deprecated use /license/setting instead""" + key_field.desc: """License string""" @@ -19,12 +31,12 @@ key_field.label: """License string""" license_root.desc: -"""Defines the EMQX Enterprise license. +"""Defines the EMQX Enterprise license. The default license has 100 connections limit, it is issued on 2023-01-09 and valid for 5 years (1825 days). -EMQX comes with a default trial license. For production use, please +EMQX comes with a default trial license. For production use, please visit https://www.emqx.com/apply-licenses/emqx to apply.""" license_root.label: