diff --git a/apps/emqx_prometheus/src/emqx_prometheus.erl b/apps/emqx_prometheus/src/emqx_prometheus.erl index 29d531140..09ba157a0 100644 --- a/apps/emqx_prometheus/src/emqx_prometheus.erl +++ b/apps/emqx_prometheus/src/emqx_prometheus.erl @@ -59,6 +59,12 @@ -export([collect/1]). +-export([ + %% For bpapi, deprecated_since 5.0.10, remove this when 5.1.x + do_start/0, + do_stop/0 +]). + -define(C(K, L), proplists:get_value(K, L, 0)). -define(TIMER_MSG, '#interval'). @@ -112,7 +118,6 @@ handle_info({update, Conf}, State = #{timer := Timer}) -> handle_info(_Msg, State) -> {noreply, State}. - push_to_push_gateway(Url, Headers) when is_list(Headers) -> Data = prometheus_text_format:format(), case httpc:request(post, {Url, Headers, "text/plain", Data}, ?HTTP_OPTIONS, []) of @@ -678,3 +683,11 @@ emqx_cluster_data() -> {nodes_running, length(Running)}, {nodes_stopped, length(Stopped)} ]. + +%% deprecated_since 5.0.10, remove this when 5.1.x +do_start() -> + emqx_prometheus_sup:start_child(?APP). + +%% deprecated_since 5.0.10, remove this when 5.1.x +do_stop() -> + emqx_prometheus_sup:stop_child(?APP). diff --git a/apps/emqx_prometheus/src/emqx_prometheus_config.erl b/apps/emqx_prometheus/src/emqx_prometheus_config.erl index 27f654bea..dccf28af0 100644 --- a/apps/emqx_prometheus/src/emqx_prometheus_config.erl +++ b/apps/emqx_prometheus/src/emqx_prometheus_config.erl @@ -20,7 +20,7 @@ -include("emqx_prometheus.hrl"). -export([add_handler/0, remove_handler/0]). --export([post_config_update/5]). +-export([pre_config_update/3, post_config_update/5]). -export([update/1]). -export([conf/0, is_push_gateway_server_enabled/1]). @@ -46,6 +46,54 @@ remove_handler() -> ok = emqx_config_handler:remove_handler(?PROMETHEUS), ok. +%% when we import the config with the old version +%% we need to respect it, and convert to new schema. +pre_config_update(?PROMETHEUS, MergeConf, OriginConf) -> + OriginType = emqx_prometheus_schema:is_recommend_type(OriginConf), + MergeType = emqx_prometheus_schema:is_recommend_type(MergeConf), + {ok, + case {OriginType, MergeType} of + {true, false} -> to_recommend_type(MergeConf); + _ -> MergeConf + end}. + +to_recommend_type(Conf) -> + #{ + <<"push_gateway">> => to_push_gateway(Conf), + <<"collectors">> => to_collectors(Conf) + }. + +to_push_gateway(Conf) -> + Init = maps:with([<<"interval">>, <<"headers">>, <<"job_name">>], Conf), + case maps:get(<<"push_gateway_server">>, Conf, "") of + "" -> + Init#{<<"url">> => <<"">>}; + Url -> + case maps:get(<<"enable">>, Conf, false) of + false -> Init#{<<"url">> => <<"">>}; + true -> Init#{<<"url">> => Url} + end + end. + +to_collectors(Conf) -> + lists:foldl( + fun({From, To}, Acc) -> + case maps:find(From, Conf) of + {ok, Value} -> Acc#{To => Value}; + error -> Acc + end + end, + #{}, + [ + {<<"vm_dist_collector">>, <<"vm_dist">>}, + {<<"mnesia_collector">>, <<"mnesia">>}, + {<<"vm_statistics_collector">>, <<"vm_statistics">>}, + {<<"vm_system_info_collector">>, <<"vm_system_info">>}, + {<<"vm_memory_collector">>, <<"vm_memory">>}, + {<<"vm_msacc_collector">>, <<"vm_msacc">>} + ] + ). + post_config_update(?PROMETHEUS, _Req, New, Old, AppEnvs) -> update_prometheus(AppEnvs), _ = update_push_gateway(New), diff --git a/apps/emqx_prometheus/src/emqx_prometheus_schema.erl b/apps/emqx_prometheus/src/emqx_prometheus_schema.erl index 1e67b06ca..533b26c9f 100644 --- a/apps/emqx_prometheus/src/emqx_prometheus_schema.erl +++ b/apps/emqx_prometheus/src/emqx_prometheus_schema.erl @@ -27,7 +27,8 @@ desc/1, translation/1, convert_headers/2, - validate_url/1 + validate_url/1, + is_recommend_type/1 ]). namespace() -> prometheus. @@ -63,7 +64,7 @@ fields(recommend_setting) -> } )}, {collectors, - ?HOCON(?R_REF(collector), #{ + ?HOCON(?R_REF(collectors), #{ required => false, importance => ?IMPORTANCE_LOW, desc => ?DESC(collectors) @@ -110,7 +111,7 @@ fields(push_gateway) -> } )} ]; -fields(collector) -> +fields(collectors) -> [ {vm_dist, ?HOCON( @@ -295,13 +296,35 @@ setting_union_schema() -> RecommendSetting = ?R_REF(recommend_setting), LegacySetting = ?R_REF(legacy_deprecated_setting), fun - (all_union_members) -> [RecommendSetting, LegacySetting]; - ({value, #{<<"enable">> := _}}) -> [LegacySetting]; - %% all other cases treat as new config, include init empty config. - ({value, _}) -> [RecommendSetting] + (all_union_members) -> + [RecommendSetting, LegacySetting]; + ({value, Setting}) -> + case is_recommend_type(Setting) of + true -> [RecommendSetting]; + false -> [LegacySetting] + end + end. + +%% For it to be considered as new schema, +%% all keys must be included in the new configuration. +is_recommend_type(Setting) -> + case maps:keys(Setting) of + [] -> + true; + Keys -> + NewKeys = fields(recommend_setting), + Fun = fun(Key0) -> + Key = binary_to_existing_atom(Key0), + lists:keymember(Key, 1, NewKeys) + end, + lists:all(Fun, Keys) end. desc(prometheus) -> ?DESC(prometheus); +desc(collectors) -> ?DESC(collectors); +desc(legacy_deprecated_setting) -> ?DESC(legacy_deprecated_setting); +desc(recommend_setting) -> ?DESC(recommend_setting); +desc(push_gateway) -> ?DESC(push_gateway); desc(_) -> undefined. convert_headers(undefined, _) -> diff --git a/apps/emqx_prometheus/src/emqx_prometheus_sup.erl b/apps/emqx_prometheus/src/emqx_prometheus_sup.erl index e8404fc6b..ea8a2ebaa 100644 --- a/apps/emqx_prometheus/src/emqx_prometheus_sup.erl +++ b/apps/emqx_prometheus/src/emqx_prometheus_sup.erl @@ -20,6 +20,7 @@ -export([ start_link/0, + start_child/1, start_child/2, update_child/2, stop_child/1 @@ -40,6 +41,10 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). +-spec start_child(atom()) -> ok. +start_child(Mod) when is_atom(Mod) -> + start_child(Mod, emqx_prometheus_config:conf()). + -spec start_child(atom(), map()) -> ok. start_child(Mod, Conf) when is_atom(Mod) -> assert_started(supervisor:start_child(?MODULE, ?CHILD(Mod, Conf))). diff --git a/apps/emqx_prometheus/src/proto/emqx_prometheus_proto_v1.erl b/apps/emqx_prometheus/src/proto/emqx_prometheus_proto_v1.erl new file mode 100644 index 000000000..b3ba1b6ce --- /dev/null +++ b/apps/emqx_prometheus/src/proto/emqx_prometheus_proto_v1.erl @@ -0,0 +1,41 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_prometheus_proto_v1). + +-behaviour(emqx_bpapi). + +-export([ + introduced_in/0, + deprecated_since/0, + start/1, + stop/1 +]). + +-include_lib("emqx/include/bpapi.hrl"). + +deprecated_since() -> "5.0.10". + +introduced_in() -> + "5.0.0". + +-spec start([node()]) -> emqx_rpc:multicall_result(). +start(Nodes) -> + rpc:multicall(Nodes, emqx_prometheus, do_start, [], 5000). + +-spec stop([node()]) -> emqx_rpc:multicall_result(). +stop(Nodes) -> + rpc:multicall(Nodes, emqx_prometheus, do_stop, [], 5000). diff --git a/apps/emqx_prometheus/test/emqx_prometheus_SUITE.erl b/apps/emqx_prometheus/test/emqx_prometheus_SUITE.erl index 4ac935604..311233f1b 100644 --- a/apps/emqx_prometheus/test/emqx_prometheus_SUITE.erl +++ b/apps/emqx_prometheus/test/emqx_prometheus_SUITE.erl @@ -219,5 +219,5 @@ init(Req0, Opts) -> Headers ), RespHeader = #{<<"content-type">> => <<"text/plain; charset=utf-8">>}, - Req = -cowboy_req:reply(200, RespHeader, <<"OK">>, Req0), + Req = cowboy_req:reply(200, RespHeader, <<"OK">>, Req0), {ok, Req, Opts}. diff --git a/changes/ce/feat-11884.en.md b/changes/ce/feat-11884.en.md new file mode 100644 index 000000000..66d2b0a8e --- /dev/null +++ b/changes/ce/feat-11884.en.md @@ -0,0 +1,4 @@ +Modified the Prometheus API and configuration to: +- Restructure configuration sections to group related settings, improving readability and maintainability +- Introduced `enable_basic_auth` configuration for basic authentication on the scrape API endpoint, enhancing security +- Maintained backwards compatibility while refactoring code, avoiding breaking changes diff --git a/rel/i18n/emqx_prometheus_schema.hocon b/rel/i18n/emqx_prometheus_schema.hocon index f5cb2ad8e..57d6b8065 100644 --- a/rel/i18n/emqx_prometheus_schema.hocon +++ b/rel/i18n/emqx_prometheus_schema.hocon @@ -17,7 +17,7 @@ Default value is: ${name}/instance/${name}~${host}""" prometheus.desc: """EMQX's Prometheus scraping endpoint is enabled by default without authentication. -You can inspect it with a `curl` command like this: `curl -f "127.0.0.1:18083/api/v5/prometheus/stats"`
""" +You can inspect it with a `curl` command like this: `curl -f "127.0.0.1:18083/api/v5/prometheus/stats"`""" prometheus.label: """Prometheus""" @@ -25,11 +25,17 @@ prometheus.label: push_gateway.desc: """Push Gateway is optional, should not be configured if prometheus is to scrape EMQX.""" +enable_basic_auth.desc: +"""Enable or disable basic authentication for prometheus scrape api, not for Push Gateway""" + collectors.desc: """The internal advanced metrics of the virtual machine are initially disabled and are usually only enabled during performance testing. Enabling them will increase the CPU load.""" +recommend_setting.desc: +"""Recommended setting""" + push_gateway_url.desc: """URL of Pushgateway server. Pushgateway is optional, should not be configured if prometheus is to scrape EMQX. Set url to "" to disable push gateway""" @@ -54,37 +60,43 @@ vm_statistics_collector.desc: vm_system_info_collector.desc: """Enable or disable VM system info collector.""" +legacy_deprecated_setting.desc: +"""Deprecated""" + legacy_enable.desc: -"""Deprecated, use prometheus.push_gateway.url instead""" +"""Deprecated, use `prometheus.push_gateway.url` instead""" legacy_headers.desc: -"""Deprecated, use prometheus.push_gateway.headers instead""" +"""Deprecated, use `prometheus.push_gateway.headers` instead""" legacy_interval.desc: -"""Deprecated, use prometheus.push_gateway.interval instead""" +"""Deprecated, use `prometheus.push_gateway.interval` instead""" legacy_job_name.desc: -"""Deprecated, use prometheus.push_gateway.job_name instead""" +"""Deprecated, use `prometheus.push_gateway.job_name` instead""" legacy_push_gateway_server.desc: -"""Deprecated, use prometheus.push_gateway.url instead""" +"""Deprecated, use `prometheus.push_gateway.url` instead""" legacy_mnesia_collector.desc: -"""Deprecated, use prometheus.collectors.mnesia instead""" +"""Deprecated, use `prometheus.collectors.mnesia` instead""" legacy_vm_dist_collector.desc: -"""Deprecated, use prometheus.collectors.vm_dist instead""" +"""Deprecated, use `prometheus.collectors.vm_dist` instead""" legacy_vm_memory_collector.desc: -"""Deprecated, use prometheus.collectors.vm_memory instead""" +"""Deprecated, use `prometheus.collectors.vm_memory` instead""" legacy_vm_msacc_collector.desc: -"""Deprecated, use prometheus.collectors.vm_msacc instead""" +"""Deprecated, use `prometheus.collectors.vm_msacc` instead""" legacy_vm_statistics_collector.desc: -"""Deprecated, use prometheus.collectors.vm_statistics instead""" +"""Deprecated, use `prometheus.collectors.vm_statistics` instead""" legacy_vm_system_info_collector.desc: -"""Deprecated, use prometheus.collectors.vm_system_info instead""" +"""Deprecated, use `prometheus.collectors.vm_system_info` instead""" + +legacy_deprecated_setting.desc: +"""Deprecated""" } diff --git a/scripts/spellcheck/dicts/emqx.txt b/scripts/spellcheck/dicts/emqx.txt index 5630404c3..bc05df68a 100644 --- a/scripts/spellcheck/dicts/emqx.txt +++ b/scripts/spellcheck/dicts/emqx.txt @@ -295,3 +295,4 @@ dnstream upstream priv Syskeeper +msacc