Merge pull request #11249 from zhongwencool/license-setting-api

feat: add license setting get/put api
This commit is contained in:
zhongwencool 2023-07-12 18:34:23 +08:00 committed by GitHub
commit 5b0695ca19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 150 additions and 23 deletions

View File

@ -1,6 +1,6 @@
{application, emqx_enterprise, [ {application, emqx_enterprise, [
{description, "EMQX Enterprise Edition"}, {description, "EMQX Enterprise Edition"},
{vsn, "0.1.1"}, {vsn, "0.1.2"},
{registered, []}, {registered, []},
{applications, [ {applications, [
kernel, kernel,

View File

@ -35,7 +35,7 @@ desc(Name) ->
ee_delegate(desc, ?EE_SCHEMA_MODULES, Name). ee_delegate(desc, ?EE_SCHEMA_MODULES, Name).
validations() -> validations() ->
emqx_conf_schema:validations(). emqx_conf_schema:validations() ++ emqx_license_schema:validations().
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% helpers %% helpers

View File

@ -0,0 +1 @@
Support HTTP API for setting alarm watermark of license.

View File

@ -22,7 +22,8 @@
unload/0, unload/0,
read_license/0, read_license/0,
read_license/1, read_license/1,
update_key/1 update_key/1,
update_setting/1
]). ]).
-define(CONF_KEY_PATH, [license]). -define(CONF_KEY_PATH, [license]).
@ -64,6 +65,14 @@ update_key(Value) when is_binary(Value); is_list(Value) ->
), ),
handle_config_update_result(Result). 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 %% emqx_hooks
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -96,6 +105,8 @@ check(_ConnInfo, AckProps) ->
pre_config_update(_, Cmd, Conf) -> pre_config_update(_, Cmd, Conf) ->
{ok, do_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) -> post_config_update(_Path, _Cmd, NewConf, _Old, _AppEnvs) ->
case read_license(NewConf) of case read_license(NewConf) of
{ok, License} -> {ok, License} ->
@ -122,6 +133,8 @@ do_update({key, Content}, Conf) when is_binary(Content); is_list(Content) ->
{error, Reason} -> {error, Reason} ->
erlang:throw(Reason) erlang:throw(Reason)
end; end;
do_update({setting, Setting}, Conf) ->
maps:merge(Conf, Setting);
do_update(NewConf, _PrevConf) -> do_update(NewConf, _PrevConf) ->
#{<<"key">> := NewKey} = NewConf, #{<<"key">> := NewKey} = NewConf,
do_update({key, NewKey}, NewConf). do_update({key, NewKey}, NewConf).

View File

@ -13,11 +13,14 @@
namespace/0, namespace/0,
api_spec/0, api_spec/0,
paths/0, paths/0,
schema/1 schema/1,
fields/1
]). ]).
-define(LICENSE_TAGS, [<<"License">>]).
-export([ -export([
'/license'/2 '/license'/2,
'/license/setting'/2
]). ]).
-define(BAD_REQUEST, 'BAD_REQUEST'). -define(BAD_REQUEST, 'BAD_REQUEST').
@ -29,14 +32,15 @@ api_spec() ->
paths() -> paths() ->
[ [
"/license" "/license",
"/license/setting"
]. ].
schema("/license") -> schema("/license") ->
#{ #{
'operationId' => '/license', 'operationId' => '/license',
get => #{ get => #{
tags => [<<"license">>], tags => ?LICENSE_TAGS,
summary => <<"Get license info">>, summary => <<"Get license info">>,
description => ?DESC("desc_license_info_api"), description => ?DESC("desc_license_info_api"),
responses => #{ responses => #{
@ -50,19 +54,18 @@ schema("/license") ->
) )
} }
}, },
%% TODO(5.x): It's a update action, should use PUT instead
post => #{ post => #{
tags => [<<"license">>], tags => ?LICENSE_TAGS,
summary => <<"Update license key">>, summary => <<"Update license key">>,
description => ?DESC("desc_license_key_api"), description => ?DESC("desc_license_key_api"),
'requestBody' => emqx_dashboard_swagger:schema_with_examples( 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
emqx_license_schema:key_license(), hoconsc:ref(?MODULE, key_license),
#{ #{
license_key => #{ license_key => #{
summary => <<"License key string">>, summary => <<"License key string">>,
value => #{ value => #{
<<"key">> => <<"xxx">>, <<"key">> => <<"xxx">>
<<"connection_low_watermark">> => "75%",
<<"connection_high_watermark">> => "80%"
} }
} }
} }
@ -79,6 +82,28 @@ schema("/license") ->
400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad license key">>) 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() -> sample_license_info_response() ->
@ -117,3 +142,24 @@ error_msg(Code, Msg) ->
end; end;
'/license'(post, _Params) -> '/license'(post, _Params) ->
{400, error_msg(?BAD_REQUEST, <<"Invalid request 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)).

View File

@ -16,15 +16,14 @@
-export([roots/0, fields/1, validations/0, desc/1, tags/0]). -export([roots/0, fields/1, validations/0, desc/1, tags/0]).
-export([ -export([
default_license/0, default_license/0
key_license/0
]). ]).
roots() -> roots() ->
[ [
{license, {license,
hoconsc:mk( hoconsc:mk(
key_license(), hoconsc:ref(?MODULE, key_license),
#{ #{
desc => ?DESC(license_root) desc => ?DESC(license_root)
} }
@ -47,11 +46,13 @@ fields(key_license) ->
{connection_low_watermark, #{ {connection_low_watermark, #{
type => emqx_schema:percent(), type => emqx_schema:percent(),
default => <<"75%">>, default => <<"75%">>,
example => <<"75%">>,
desc => ?DESC(connection_low_watermark_field) desc => ?DESC(connection_low_watermark_field)
}}, }},
{connection_high_watermark, #{ {connection_high_watermark, #{
type => emqx_schema:percent(), type => emqx_schema:percent(),
default => <<"80%">>, default => <<"80%">>,
example => <<"80%">>,
desc => ?DESC(connection_high_watermark_field) desc => ?DESC(connection_high_watermark_field)
}} }}
]. ].
@ -64,9 +65,6 @@ desc(_) ->
validations() -> validations() ->
[{check_license_watermark, fun check_license_watermark/1}]. [{check_license_watermark, fun check_license_watermark/1}].
key_license() ->
hoconsc:ref(?MODULE, key_license).
check_license_watermark(Conf) -> check_license_watermark(Conf) ->
case hocon_maps:get("license.connection_low_watermark", Conf) of case hocon_maps:get("license.connection_low_watermark", Conf) of
undefined -> undefined ->

View File

@ -38,9 +38,15 @@ set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config(<<"license_admin">>); emqx_dashboard_api_test_helpers:set_default_config(<<"license_admin">>);
set_special_configs(emqx_license) -> set_special_configs(emqx_license) ->
LicenseKey = emqx_license_test_lib:make_license(#{max_connections => "100"}), 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), 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), emqx_config:put_raw([<<"license">>], RawConfig),
ok = persistent_term:put( ok = persistent_term:put(
emqx_license_test_pubkey, emqx_license_test_pubkey,
@ -172,3 +178,46 @@ t_license_upload_key_not_json(_Config) ->
), ),
assert_untouched_license(), assert_untouched_license(),
ok. 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])
).

View File

@ -189,7 +189,8 @@ defmodule EMQXUmbrella.MixProject do
:emqx_bridge_rabbitmq, :emqx_bridge_rabbitmq,
:emqx_bridge_clickhouse, :emqx_bridge_clickhouse,
:emqx_ft, :emqx_ft,
:emqx_s3 :emqx_s3,
:emqx_enterprise
]) ])
end end

View File

@ -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_bridge_rabbitmq") -> false;
is_community_umbrella_app("apps/emqx_ft") -> false; is_community_umbrella_app("apps/emqx_ft") -> false;
is_community_umbrella_app("apps/emqx_s3") -> false; is_community_umbrella_app("apps/emqx_s3") -> false;
is_community_umbrella_app("apps/emqx_enterprise") -> false;
is_community_umbrella_app(_) -> true. is_community_umbrella_app(_) -> true.
is_jq_supported() -> is_jq_supported() ->

View File

@ -12,4 +12,10 @@ desc_license_key_api.desc:
desc_license_key_api.label: desc_license_key_api.label:
"""Update license""" """Update license"""
desc_license_setting_api.desc:
"""Update license setting"""
desc_license_setting_api.label:
"""Update license setting"""
} }

View File

@ -12,6 +12,18 @@ connection_low_watermark_field.desc:
connection_low_watermark_field.label: connection_low_watermark_field.label:
"""Connection low watermark""" """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: key_field.desc:
"""License string""" """License string"""