summaryrefslogtreecommitdiff
path: root/deps/rabbitmq_prometheus/test
diff options
context:
space:
mode:
Diffstat (limited to 'deps/rabbitmq_prometheus/test')
-rw-r--r--deps/rabbitmq_prometheus/test/config_schema_SUITE.erl54
-rw-r--r--deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/cacert.pem1
-rw-r--r--deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/cert.pem1
-rw-r--r--deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/key.pem1
-rw-r--r--deps/rabbitmq_prometheus/test/config_schema_SUITE_data/rabbitmq_prometheus.snippets280
-rw-r--r--deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_management.schema436
-rw-r--r--deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_management_agent.schema4
-rw-r--r--deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_prometheus.schema116
-rw-r--r--deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl282
9 files changed, 1175 insertions, 0 deletions
diff --git a/deps/rabbitmq_prometheus/test/config_schema_SUITE.erl b/deps/rabbitmq_prometheus/test/config_schema_SUITE.erl
new file mode 100644
index 0000000000..97719d9246
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/config_schema_SUITE.erl
@@ -0,0 +1,54 @@
+%% This Source Code Form is subject to the terms of the Mozilla Public
+%% License, v. 2.0. If a copy of the MPL was not distributed with this
+%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
+%%
+%% Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(config_schema_SUITE).
+
+-compile(export_all).
+
+all() ->
+ [
+ run_snippets
+ ].
+
+%% -------------------------------------------------------------------
+%% Testsuite setup/teardown.
+%% -------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ rabbit_ct_helpers:log_environment(),
+ Config1 = rabbit_ct_helpers:run_setup_steps(Config),
+ rabbit_ct_config_schema:init_schemas(rabbitmq_prometheus, Config1).
+
+
+end_per_suite(Config) ->
+ rabbit_ct_helpers:run_teardown_steps(Config).
+
+init_per_testcase(Testcase, Config) ->
+ rabbit_ct_helpers:testcase_started(Config, Testcase),
+ Config1 = rabbit_ct_helpers:set_config(Config, [
+ {rmq_nodename_suffix, Testcase}
+ ]),
+ rabbit_ct_helpers:run_steps(Config1,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()).
+
+end_per_testcase(Testcase, Config) ->
+ Config1 = rabbit_ct_helpers:run_steps(Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()),
+ rabbit_ct_helpers:testcase_finished(Config1, Testcase).
+
+%% -------------------------------------------------------------------
+%% Testcases.
+%% -------------------------------------------------------------------
+
+run_snippets(Config) ->
+ ok = rabbit_ct_broker_helpers:rpc(Config, 0,
+ ?MODULE, run_snippets1, [Config]).
+
+run_snippets1(Config) ->
+ rabbit_ct_config_schema:run_snippets(Config).
diff --git a/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/cacert.pem b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/cacert.pem
new file mode 100644
index 0000000000..eaf6b67806
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/cacert.pem
@@ -0,0 +1 @@
+I'm not a certificate
diff --git a/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/cert.pem b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/cert.pem
new file mode 100644
index 0000000000..eaf6b67806
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/cert.pem
@@ -0,0 +1 @@
+I'm not a certificate
diff --git a/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/key.pem b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/key.pem
new file mode 100644
index 0000000000..eaf6b67806
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/certs/key.pem
@@ -0,0 +1 @@
+I'm not a certificate
diff --git a/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/rabbitmq_prometheus.snippets b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/rabbitmq_prometheus.snippets
new file mode 100644
index 0000000000..90b1b4c181
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/rabbitmq_prometheus.snippets
@@ -0,0 +1,280 @@
+%% This Source Code Form is subject to the terms of the Mozilla Public
+%% License, v. 2.0. If a copy of the MPL was not distributed with this
+%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
+%%
+%% Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+[
+ %%
+ %% Path
+ %%
+
+ {endpoint_path,
+ "prometheus.path = /metriczzz",
+ [{rabbitmq_prometheus,[
+ {path, "/metriczzz"}
+ ]}],
+ [rabbitmq_prometheus]},
+
+ %%
+ %% TCP listener
+ %%
+
+ {tcp_listener_port_only,
+ "prometheus.tcp.port = 15692",
+ [{rabbitmq_prometheus,[
+ {tcp_config,[
+ {port,15692}
+ ]}
+ ]}],
+ [rabbitmq_prometheus]},
+
+ {tcp_listener_interface_port,
+ "prometheus.tcp.ip = 192.168.1.2
+ prometheus.tcp.port = 15692",
+ [{rabbitmq_prometheus,[
+ {tcp_config,[
+ {ip, "192.168.1.2"},
+ {port,15692}
+ ]}
+ ]}],
+ [rabbitmq_prometheus]},
+
+ {tcp_listener_server_opts_compress,
+ "prometheus.tcp.compress = true",
+ [
+ {rabbitmq_prometheus, [
+ {tcp_config, [{cowboy_opts, [{compress, true}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tcp_listener_server_opts_compress_and_idle_timeout,
+ "prometheus.tcp.compress = true
+ prometheus.tcp.idle_timeout = 123",
+ [
+ {rabbitmq_prometheus, [
+ {tcp_config, [{cowboy_opts, [{compress, true},
+ {idle_timeout, 123}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tcp_listener_server_opts_compress_and_multiple_timeouts,
+ "prometheus.tcp.compress = true
+ prometheus.tcp.idle_timeout = 123
+ prometheus.tcp.inactivity_timeout = 456
+ prometheus.tcp.request_timeout = 789",
+ [
+ {rabbitmq_prometheus, [
+ {tcp_config, [{cowboy_opts, [{compress, true},
+ {idle_timeout, 123},
+ {inactivity_timeout, 456},
+ {request_timeout, 789}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tcp_listener_server_opts_multiple_timeouts_only,
+ "prometheus.tcp.idle_timeout = 123
+ prometheus.tcp.inactivity_timeout = 456
+ prometheus.tcp.request_timeout = 789",
+ [
+ {rabbitmq_prometheus, [
+ {tcp_config, [{cowboy_opts, [{idle_timeout, 123},
+ {inactivity_timeout, 456},
+ {request_timeout, 789}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tcp_listener_server_opts_shutdown_timeout,
+ "prometheus.tcp.shutdown_timeout = 7000",
+ [
+ {rabbitmq_prometheus, [
+ {tcp_config, [{cowboy_opts, [{shutdown_timeout, 7000}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tcp_listener_server_opts_max_keepalive,
+ "prometheus.tcp.max_keepalive = 120",
+ [
+ {rabbitmq_prometheus, [
+ {tcp_config, [{cowboy_opts, [{max_keepalive, 120}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+
+ %%
+ %% TLS listener
+ %%
+
+ {tls_listener_port_only,
+ "prometheus.ssl.port = 15691",
+ [{rabbitmq_prometheus,[
+ {ssl_config,[
+ {port,15691}
+ ]}
+ ]}],
+ [rabbitmq_prometheus]},
+
+ {tls_listener_interface_port,
+ "prometheus.ssl.ip = 192.168.1.2
+ prometheus.ssl.port = 15691",
+ [{rabbitmq_prometheus,[
+ {ssl_config,[
+ {ip, "192.168.1.2"},
+ {port,15691}
+ ]}
+ ]}],
+ [rabbitmq_prometheus]},
+
+ {tls_listener,
+ "prometheus.ssl.ip = 192.168.1.2
+ prometheus.ssl.port = 15691
+ prometheus.ssl.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem
+ prometheus.ssl.certfile = test/config_schema_SUITE_data/certs/cert.pem
+ prometheus.ssl.keyfile = test/config_schema_SUITE_data/certs/key.pem
+ prometheus.ssl.verify = verify_none
+ prometheus.ssl.fail_if_no_peer_cert = false",
+ [{rabbitmq_prometheus,[
+ {ssl_config,[
+ {ip, "192.168.1.2"},
+ {port,15691},
+ {cacertfile,"test/config_schema_SUITE_data/certs/cacert.pem"},
+ {certfile,"test/config_schema_SUITE_data/certs/cert.pem"},
+ {keyfile,"test/config_schema_SUITE_data/certs/key.pem"},
+ {verify, verify_none},
+ {fail_if_no_peer_cert, false}
+ ]}
+ ]}],
+ [rabbitmq_prometheus]},
+
+ {tls_listener_cipher_suites,
+ "prometheus.ssl.ip = 192.168.1.2
+ prometheus.ssl.port = 15691
+ prometheus.ssl.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem
+ prometheus.ssl.certfile = test/config_schema_SUITE_data/certs/cert.pem
+ prometheus.ssl.keyfile = test/config_schema_SUITE_data/certs/key.pem
+
+ prometheus.ssl.honor_cipher_order = true
+ prometheus.ssl.honor_ecc_order = true
+ prometheus.ssl.client_renegotiation = false
+ prometheus.ssl.secure_renegotiate = true
+
+ prometheus.ssl.verify = verify_peer
+ prometheus.ssl.fail_if_no_peer_cert = false
+
+ prometheus.ssl.versions.1 = tlsv1.2
+ prometheus.ssl.versions.2 = tlsv1.1
+
+ prometheus.ssl.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384
+ prometheus.ssl.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384
+ prometheus.ssl.ciphers.3 = ECDHE-ECDSA-AES256-SHA384
+ prometheus.ssl.ciphers.4 = ECDHE-RSA-AES256-SHA384
+ prometheus.ssl.ciphers.5 = ECDH-ECDSA-AES256-GCM-SHA384
+ prometheus.ssl.ciphers.6 = ECDH-RSA-AES256-GCM-SHA384
+ prometheus.ssl.ciphers.7 = ECDH-ECDSA-AES256-SHA384
+ prometheus.ssl.ciphers.8 = ECDH-RSA-AES256-SHA384
+ prometheus.ssl.ciphers.9 = DHE-RSA-AES256-GCM-SHA384",
+ [{rabbitmq_prometheus,[
+ {ssl_config,[
+ {ip, "192.168.1.2"},
+ {port,15691},
+ {cacertfile,"test/config_schema_SUITE_data/certs/cacert.pem"},
+ {certfile,"test/config_schema_SUITE_data/certs/cert.pem"},
+ {keyfile,"test/config_schema_SUITE_data/certs/key.pem"},
+
+ {verify, verify_peer},
+ {fail_if_no_peer_cert, false},
+
+ {honor_cipher_order, true},
+ {honor_ecc_order, true},
+ {client_renegotiation, false},
+ {secure_renegotiate, true},
+
+ {versions,['tlsv1.2','tlsv1.1']},
+ {ciphers, [
+ "ECDHE-ECDSA-AES256-GCM-SHA384",
+ "ECDHE-RSA-AES256-GCM-SHA384",
+ "ECDHE-ECDSA-AES256-SHA384",
+ "ECDHE-RSA-AES256-SHA384",
+ "ECDH-ECDSA-AES256-GCM-SHA384",
+ "ECDH-RSA-AES256-GCM-SHA384",
+ "ECDH-ECDSA-AES256-SHA384",
+ "ECDH-RSA-AES256-SHA384",
+ "DHE-RSA-AES256-GCM-SHA384"
+ ]}
+ ]}
+ ]}],
+ [rabbitmq_prometheus]},
+
+ {tls_listener_server_opts_compress,
+ "prometheus.ssl.compress = true",
+ [
+ {rabbitmq_prometheus, [
+ {ssl_config, [{cowboy_opts, [{compress, true}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tls_listener_server_opts_compress_and_idle_timeout,
+ "prometheus.ssl.compress = true
+ prometheus.ssl.idle_timeout = 123",
+ [
+ {rabbitmq_prometheus, [
+ {ssl_config, [{cowboy_opts, [{compress, true},
+ {idle_timeout, 123}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tls_listener_server_opts_compress_and_multiple_timeouts,
+ "prometheus.ssl.compress = true
+ prometheus.ssl.idle_timeout = 123
+ prometheus.ssl.inactivity_timeout = 456
+ prometheus.ssl.request_timeout = 789",
+ [
+ {rabbitmq_prometheus, [
+ {ssl_config, [{cowboy_opts, [{compress, true},
+ {idle_timeout, 123},
+ {inactivity_timeout, 456},
+ {request_timeout, 789}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tls_listener_server_opts_multiple_timeouts_only,
+ "prometheus.ssl.idle_timeout = 123
+ prometheus.ssl.inactivity_timeout = 456
+ prometheus.ssl.request_timeout = 789",
+ [
+ {rabbitmq_prometheus, [
+ {ssl_config, [{cowboy_opts, [{idle_timeout, 123},
+ {inactivity_timeout, 456},
+ {request_timeout, 789}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tls_listener_server_opts_shutdown_timeout,
+ "prometheus.ssl.shutdown_timeout = 7000",
+ [
+ {rabbitmq_prometheus, [
+ {ssl_config, [{cowboy_opts, [{shutdown_timeout, 7000}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ },
+
+ {tls_listener_server_opts_max_keepalive,
+ "prometheus.ssl.max_keepalive = 120",
+ [
+ {rabbitmq_prometheus, [
+ {ssl_config, [{cowboy_opts, [{max_keepalive, 120}]}]}
+ ]}
+ ], [rabbitmq_prometheus]
+ }
+].
diff --git a/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_management.schema b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_management.schema
new file mode 100644
index 0000000000..e05da0a001
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_management.schema
@@ -0,0 +1,436 @@
+%% ----------------------------------------------------------------------------
+%% RabbitMQ Management Plugin
+%%
+%% See https://www.rabbitmq.com/management.html for details
+%% ----------------------------------------------------------------------------
+
+%% Load definitions from a JSON file or directory of files. See
+%% https://www.rabbitmq.com/management.html#load-definitions
+%%
+%% {load_definitions, "/path/to/schema.json"},
+%% {load_definitions, "/path/to/schemas"},
+{mapping, "management.load_definitions", "rabbitmq_management.load_definitions",
+ [{datatype, string},
+ {validators, ["file_accessible"]}]}.
+
+%% Log all requests to the management HTTP API to a file.
+%%
+%% {http_log_dir, "/path/to/access.log"},
+
+{mapping, "management.http_log_dir", "rabbitmq_management.http_log_dir",
+ [{datatype, string}]}.
+
+%% HTTP (TCP) listener options ========================================================
+
+%% HTTP listener consistent with Web STOMP and Web MQTT.
+%%
+%% {tcp_config, [{port, 15672},
+%% {ip, "127.0.0.1"}]}
+
+{mapping, "management.tcp.port", "rabbitmq_management.tcp_config.port",
+ [{datatype, integer}]}.
+{mapping, "management.tcp.ip", "rabbitmq_management.tcp_config.ip",
+ [{datatype, string},
+ {validators, ["is_ip"]}]}.
+
+{mapping, "management.tcp.compress", "rabbitmq_management.tcp_config.cowboy_opts.compress",
+ [{datatype, {enum, [true, false]}}]}.
+{mapping, "management.tcp.idle_timeout", "rabbitmq_management.tcp_config.cowboy_opts.idle_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "management.tcp.inactivity_timeout", "rabbitmq_management.tcp_config.cowboy_opts.inactivity_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "management.tcp.request_timeout", "rabbitmq_management.tcp_config.cowboy_opts.request_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "management.tcp.shutdown_timeout", "rabbitmq_management.tcp_config.cowboy_opts.shutdown_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "management.tcp.max_keepalive", "rabbitmq_management.tcp_config.cowboy_opts.max_keepalive",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+
+
+%% HTTPS (TLS) listener options ========================================================
+
+%% HTTPS listener consistent with Web STOMP and Web MQTT.
+%%
+%% {ssl_config, [{port, 15671},
+%% {ip, "127.0.0.1"},
+%% {cacertfile, "/path/to/cacert.pem"},
+%% {certfile, "/path/to/cert.pem"},
+%% {keyfile, "/path/to/key.pem"}]}
+
+{mapping, "management.ssl.port", "rabbitmq_management.ssl_config.port",
+ [{datatype, integer}]}.
+{mapping, "management.ssl.backlog", "rabbitmq_management.ssl_config.backlog",
+ [{datatype, integer}]}.
+{mapping, "management.ssl.ip", "rabbitmq_management.ssl_config.ip",
+ [{datatype, string}, {validators, ["is_ip"]}]}.
+{mapping, "management.ssl.certfile", "rabbitmq_management.ssl_config.certfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+{mapping, "management.ssl.keyfile", "rabbitmq_management.ssl_config.keyfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+{mapping, "management.ssl.cacertfile", "rabbitmq_management.ssl_config.cacertfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+{mapping, "management.ssl.password", "rabbitmq_management.ssl_config.password",
+ [{datatype, string}]}.
+
+{mapping, "management.ssl.verify", "rabbitmq_management.ssl_config.verify", [
+ {datatype, {enum, [verify_peer, verify_none]}}]}.
+
+{mapping, "management.ssl.fail_if_no_peer_cert", "rabbitmq_management.ssl_config.fail_if_no_peer_cert", [
+ {datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.ssl.honor_cipher_order", "rabbitmq_management.ssl_config.honor_cipher_order",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.ssl.honor_ecc_order", "rabbitmq_management.ssl_config.honor_ecc_order",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.ssl.reuse_sessions", "rabbitmq_management.ssl_config.reuse_sessions",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.ssl.secure_renegotiate", "rabbitmq_management.ssl_config.secure_renegotiate",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.ssl.client_renegotiation", "rabbitmq_management.ssl_config.client_renegotiation",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.ssl.depth", "rabbitmq_management.ssl_config.depth",
+ [{datatype, integer}, {validators, ["byte"]}]}.
+
+{mapping, "management.ssl.versions.$version", "rabbitmq_management.ssl_config.versions",
+ [{datatype, atom}]}.
+
+{translation, "rabbitmq_management.ssl_config.versions",
+fun(Conf) ->
+ Settings = cuttlefish_variable:filter_by_prefix("management.ssl.versions", Conf),
+ [V || {_, V} <- Settings]
+end}.
+
+{mapping, "management.ssl.ciphers.$cipher", "rabbitmq_management.ssl_config.ciphers",
+ [{datatype, string}]}.
+
+{translation, "rabbitmq_management.ssl_config.ciphers",
+fun(Conf) ->
+ Settings = cuttlefish_variable:filter_by_prefix("management.ssl.ciphers", Conf),
+ lists:reverse([V || {_, V} <- Settings])
+end}.
+
+{mapping, "management.ssl.compress", "rabbitmq_management.ssl_config.cowboy_opts.compress",
+ [{datatype, {enum, [true, false]}}]}.
+{mapping, "management.ssl.idle_timeout", "rabbitmq_management.ssl_config.cowboy_opts.idle_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "management.ssl.inactivity_timeout", "rabbitmq_management.ssl_config.cowboy_opts.inactivity_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "management.ssl.request_timeout", "rabbitmq_management.ssl_config.cowboy_opts.request_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "management.ssl.shutdown_timeout", "rabbitmq_management.ssl_config.cowboy_opts.shutdown_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "management.ssl.max_keepalive", "rabbitmq_management.ssl_config.cowboy_opts.max_keepalive",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+
+
+
+%% Legacy listener options ========================================================
+
+%% Legacy (pre-3.7.9) TCP listener format.
+%%
+%% {listener, [{port, 12345},
+%% {ip, "127.0.0.1"},
+%% {ssl, true},
+%% {ssl_opts, [{cacertfile, "/path/to/cacert.pem"},
+%% {certfile, "/path/to/cert.pem"},
+%% {keyfile, "/path/to/key.pem"}]}]},
+
+{mapping, "management.listener.port", "rabbitmq_management.listener.port",
+ [{datatype, integer}]}.
+
+{mapping, "management.listener.ip", "rabbitmq_management.listener.ip",
+ [{datatype, string},
+ {validators, ["is_ip"]}]}.
+
+{mapping, "management.listener.ssl", "rabbitmq_management.listener.ssl",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.listener.server.compress", "rabbitmq_management.listener.cowboy_opts.compress",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.listener.server.idle_timeout", "rabbitmq_management.listener.cowboy_opts.idle_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+
+{mapping, "management.listener.server.inactivity_timeout", "rabbitmq_management.listener.cowboy_opts.inactivity_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+
+{mapping, "management.listener.server.request_timeout", "rabbitmq_management.listener.cowboy_opts.request_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+
+{mapping, "management.listener.server.shutdown_timeout", "rabbitmq_management.listener.cowboy_opts.shutdown_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+
+{mapping, "management.listener.server.max_keepalive", "rabbitmq_management.listener.cowboy_opts.max_keepalive",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+
+%% Legacy HTTPS listener options ========================================================
+
+{mapping, "management.listener.ssl_opts", "rabbitmq_management.listener.ssl_opts", [
+ {datatype, {enum, [none]}}
+]}.
+
+{translation, "rabbitmq_management.listener.ssl_opts",
+fun(Conf) ->
+ case cuttlefish:conf_get("management.listener.ssl_opts", Conf, undefined) of
+ none -> [];
+ _ -> cuttlefish:invalid("Invalid management.listener.ssl_opts")
+ end
+end}.
+
+{mapping, "management.listener.ssl_opts.verify", "rabbitmq_management.listener.ssl_opts.verify", [
+ {datatype, {enum, [verify_peer, verify_none]}}]}.
+
+{mapping, "management.listener.ssl_opts.fail_if_no_peer_cert", "rabbitmq_management.listener.ssl_opts.fail_if_no_peer_cert", [
+ {datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.listener.ssl_opts.cacertfile", "rabbitmq_management.listener.ssl_opts.cacertfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+
+{mapping, "management.listener.ssl_opts.certfile", "rabbitmq_management.listener.ssl_opts.certfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+
+{mapping, "management.listener.ssl_opts.cacerts.$name", "rabbitmq_management.listener.ssl_opts.cacerts",
+ [{datatype, string}]}.
+
+{translation, "rabbitmq_management.listener.ssl_opts.cacerts",
+fun(Conf) ->
+ Settings = cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.cacerts", Conf),
+ [ list_to_binary(V) || {_, V} <- Settings ]
+end}.
+
+{mapping, "management.listener.ssl_opts.honor_cipher_order", "rabbitmq_management.listener.ssl_opts.honor_cipher_order",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.listener.ssl_opts.honor_ecc_order", "rabbitmq_management.listener.ssl_opts.honor_ecc_order",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.listener.ssl_opts.reuse_sessions", "rabbitmq_management.listener.ssl_opts.reuse_sessions",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.listener.ssl_opts.secure_renegotiate", "rabbitmq_management.listener.ssl_opts.secure_renegotiate",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.listener.ssl_opts.client_renegotiation", "rabbitmq_management.listener.ssl_opts.client_renegotiation",
+ [{datatype, {enum, [true, false]}}]}.
+
+
+{mapping, "management.listener.ssl_opts.versions.$version", "rabbitmq_management.listener.ssl_opts.versions",
+ [{datatype, atom}]}.
+
+{translation, "rabbitmq_management.listener.ssl_opts.versions",
+fun(Conf) ->
+ Settings = cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.versions", Conf),
+ [ V || {_, V} <- Settings ]
+end}.
+
+
+{mapping, "management.listener.ssl_opts.cert", "rabbitmq_management.listener.ssl_opts.cert",
+ [{datatype, string}]}.
+
+{translation, "rabbitmq_management.listener.ssl_opts.cert",
+fun(Conf) ->
+ list_to_binary(cuttlefish:conf_get("management.listener.ssl_opts.cert", Conf))
+end}.
+
+{mapping, "management.listener.ssl_opts.crl_check", "rabbitmq_management.listener.ssl_opts.crl_check",
+ [{datatype, [{enum, [true, false, peer, best_effort]}]}]}.
+
+{mapping, "management.listener.ssl_opts.depth", "rabbitmq_management.listener.ssl_opts.depth",
+ [{datatype, integer}, {validators, ["byte"]}]}.
+
+{mapping, "management.listener.ssl_opts.dh", "rabbitmq_management.listener.ssl_opts.dh",
+ [{datatype, string}]}.
+
+{translation, "rabbitmq_management.listener.ssl_opts.dh",
+fun(Conf) ->
+ list_to_binary(cuttlefish:conf_get("management.listener.ssl_opts.dh", Conf))
+end}.
+
+{mapping, "management.listener.ssl_opts.dhfile", "rabbitmq_management.listener.ssl_opts.dhfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+
+{mapping, "management.listener.ssl_opts.key.RSAPrivateKey", "rabbitmq_management.listener.ssl_opts.key",
+ [{datatype, string}]}.
+
+{mapping, "management.listener.ssl_opts.key.DSAPrivateKey", "rabbitmq_management.listener.ssl_opts.key",
+ [{datatype, string}]}.
+
+{mapping, "management.listener.ssl_opts.key.PrivateKeyInfo", "rabbitmq_management.listener.ssl_opts.key",
+ [{datatype, string}]}.
+
+{translation, "rabbitmq_management.listener.ssl_opts.key",
+fun(Conf) ->
+ case cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.key", Conf) of
+ [{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)};
+ _ -> undefined
+ end
+end}.
+
+{mapping, "management.listener.ssl_opts.keyfile", "rabbitmq_management.listener.ssl_opts.keyfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+
+{mapping, "management.listener.ssl_opts.log_alert", "rabbitmq_management.listener.ssl_opts.log_alert",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.listener.ssl_opts.password", "rabbitmq_management.listener.ssl_opts.password",
+ [{datatype, string}]}.
+
+{mapping, "management.listener.ssl_opts.psk_identity", "rabbitmq_management.listener.ssl_opts.psk_identity",
+ [{datatype, string}]}.
+
+
+%% A custom path prefix for all HTTP request handlers.
+%%
+%% {path_prefix, "/a/prefix"},
+
+{mapping, "management.path_prefix", "rabbitmq_management.path_prefix",
+ [{datatype, string}]}.
+
+%% Login session timeout in minutes
+
+{mapping, "management.login_session_timeout", "rabbitmq_management.login_session_timeout", [
+ {datatype, integer}, {validators, ["non_negative_integer"]}
+]}.
+
+%% CORS
+
+{mapping, "management.cors.allow_origins", "rabbitmq_management.cors_allow_origins", [
+ {datatype, {enum, [none]}}
+]}.
+
+{mapping, "management.cors.allow_origins.$name", "rabbitmq_management.cors_allow_origins", [
+ {datatype, string}
+]}.
+
+{translation, "rabbitmq_management.cors_allow_origins",
+fun(Conf) ->
+ case cuttlefish:conf_get("management.cors.allow_origins", Conf, undefined) of
+ none -> [];
+ _ ->
+ Settings = cuttlefish_variable:filter_by_prefix("management.cors.allow_origins", Conf),
+ [V || {_, V} <- Settings]
+ end
+end}.
+
+
+{mapping, "management.cors.max_age", "rabbitmq_management.cors_max_age", [
+ {datatype, integer}, {validators, ["non_negative_integer"]}
+]}.
+
+{translation, "rabbitmq_management.cors_max_age",
+fun(Conf) ->
+ case cuttlefish:conf_get("management.cors.max_age", Conf, undefined) of
+ undefined -> cuttlefish:unset();
+ Value -> Value
+ end
+end}.
+
+
+%% CSP (https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
+
+{mapping, "management.csp.policy", "rabbitmq_management.content_security_policy", [
+ {datatype, string}
+]}.
+
+{translation, "rabbitmq_management.content_security_policy",
+fun(Conf) ->
+ case cuttlefish:conf_get("management.csp.policy", Conf, undefined) of
+ undefined -> cuttlefish:unset();
+ Value -> Value
+ end
+end}.
+
+
+%% HSTS (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)
+
+{mapping, "management.hsts.policy", "rabbitmq_management.strict_transport_security", [
+ {datatype, string}
+]}.
+
+{translation, "rabbitmq_management.strict_transport_security",
+fun(Conf) ->
+ case cuttlefish:conf_get("management.hsts.policy", Conf, undefined) of
+ undefined -> cuttlefish:unset();
+ Value -> Value
+ end
+end}.
+
+%% OAuth 2/SSO access only
+
+{mapping, "management.disable_basic_auth", "rabbitmq_management.disable_basic_auth",
+ [{datatype, {enum, [true, false]}}]}.
+
+%% Management only
+
+{mapping, "management.disable_stats", "rabbitmq_management.disable_management_stats", [
+ {datatype, {enum, [true, false]}}
+]}.
+
+{mapping, "management.enable_queue_totals", "rabbitmq_management.enable_queue_totals", [
+ {datatype, {enum, [true, false]}}]}.
+
+%% ===========================================================================
+%% Authorization
+
+{mapping, "management.enable_uaa", "rabbitmq_management.enable_uaa",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "management.uaa_client_id", "rabbitmq_management.uaa_client_id",
+ [{datatype, string}]}.
+
+{mapping, "management.uaa_location", "rabbitmq_management.uaa_location",
+ [{datatype, string}]}.
+
+%% ===========================================================================
+
+
+%% One of 'basic', 'detailed' or 'none'. See
+%% https://www.rabbitmq.com/management.html#fine-stats for more details.
+%% {rates_mode, basic},
+{mapping, "management.rates_mode", "rabbitmq_management.rates_mode",
+ [{datatype, {enum, [basic, detailed, none]}}]}.
+
+%% Configure how long aggregated data (such as message rates and queue
+%% lengths) is retained. Please read the plugin's documentation in
+%% https://www.rabbitmq.com/management.html#configuration for more
+%% details.
+%%
+%% {sample_retention_policies,
+%% [{global, [{60, 5}, {3600, 60}, {86400, 1200}]},
+%% {basic, [{60, 5}, {3600, 60}]},
+%% {detailed, [{10, 5}]}]}
+% ]},
+
+{mapping, "management.sample_retention_policies.$section.$interval",
+ "rabbitmq_management.sample_retention_policies",
+ [{datatype, integer}]}.
+
+{translation, "rabbitmq_management.sample_retention_policies",
+fun(Conf) ->
+ Global = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.global", Conf),
+ Basic = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.basic", Conf),
+ Detailed = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.detailed", Conf),
+ TranslateKey = fun("minute") -> 60;
+ ("hour") -> 3600;
+ ("day") -> 86400;
+ (Other) -> list_to_integer(Other)
+ end,
+ TranslatePolicy = fun(Section) ->
+ [ {TranslateKey(Key), Val} || {[_,_,_,Key], Val} <- Section ]
+ end,
+ [{global, TranslatePolicy(Global)},
+ {basic, TranslatePolicy(Basic)},
+ {detailed, TranslatePolicy(Detailed)}]
+end}.
+
+
+{validator, "is_dir", "is not directory",
+fun(File) ->
+ ReadFile = file:list_dir(File),
+ element(1, ReadFile) == ok
+end}.
diff --git a/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_management_agent.schema b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_management_agent.schema
new file mode 100644
index 0000000000..fa8a76725a
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_management_agent.schema
@@ -0,0 +1,4 @@
+%% Agent collectors won't start if metrics collection is disabled, only external stats are enabled.
+%% Also the management application will refuse to start if metrics collection is disabled
+{mapping, "management_agent.disable_metrics_collector", "rabbitmq_management_agent.disable_metrics_collector",
+ [{datatype, {enum, [true, false]}}]}.
diff --git a/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_prometheus.schema b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_prometheus.schema
new file mode 100644
index 0000000000..4f1028a2a6
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/config_schema_SUITE_data/schema/rabbitmq_prometheus.schema
@@ -0,0 +1,116 @@
+%% ----------------------------------------------------------------------------
+%% RabbitMQ Prometheus Plugin
+%%
+%% See https://rabbitmq.com/prometheus.html for details
+%% ----------------------------------------------------------------------------
+
+%% Endpoint path
+{mapping, "prometheus.path", "rabbitmq_prometheus.path",
+ [{datatype, string}]}.
+
+%% HTTP (TCP) listener options ========================================================
+
+%% HTTP listener consistent with the management plugin, Web STOMP and Web MQTT.
+%%
+%% {tcp_config, [{port, 15692},
+%% {ip, "127.0.0.1"}]}
+
+{mapping, "prometheus.tcp.port", "rabbitmq_prometheus.tcp_config.port",
+ [{datatype, integer}]}.
+{mapping, "prometheus.tcp.ip", "rabbitmq_prometheus.tcp_config.ip",
+ [{datatype, string},
+ {validators, ["is_ip"]}]}.
+
+{mapping, "prometheus.tcp.compress", "rabbitmq_prometheus.tcp_config.cowboy_opts.compress",
+ [{datatype, {enum, [true, false]}}]}.
+{mapping, "prometheus.tcp.idle_timeout", "rabbitmq_prometheus.tcp_config.cowboy_opts.idle_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "prometheus.tcp.inactivity_timeout", "rabbitmq_prometheus.tcp_config.cowboy_opts.inactivity_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "prometheus.tcp.request_timeout", "rabbitmq_prometheus.tcp_config.cowboy_opts.request_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "prometheus.tcp.shutdown_timeout", "rabbitmq_prometheus.tcp_config.cowboy_opts.shutdown_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "prometheus.tcp.max_keepalive", "rabbitmq_prometheus.tcp_config.cowboy_opts.max_keepalive",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+
+
+%% HTTPS (TLS) listener options ========================================================
+
+%% HTTPS listener consistent with the management plugin, Web STOMP and Web MQTT.
+%%
+%% {ssl_config, [{port, 15691},
+%% {ip, "127.0.0.1"},
+%% {cacertfile, "/path/to/cacert.pem"},
+%% {certfile, "/path/to/cert.pem"},
+%% {keyfile, "/path/to/key.pem"}]}
+
+{mapping, "prometheus.ssl.port", "rabbitmq_prometheus.ssl_config.port",
+ [{datatype, integer}]}.
+{mapping, "prometheus.ssl.backlog", "rabbitmq_prometheus.ssl_config.backlog",
+ [{datatype, integer}]}.
+{mapping, "prometheus.ssl.ip", "rabbitmq_prometheus.ssl_config.ip",
+ [{datatype, string}, {validators, ["is_ip"]}]}.
+{mapping, "prometheus.ssl.certfile", "rabbitmq_prometheus.ssl_config.certfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+{mapping, "prometheus.ssl.keyfile", "rabbitmq_prometheus.ssl_config.keyfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+{mapping, "prometheus.ssl.cacertfile", "rabbitmq_prometheus.ssl_config.cacertfile",
+ [{datatype, string}, {validators, ["file_accessible"]}]}.
+{mapping, "prometheus.ssl.password", "rabbitmq_prometheus.ssl_config.password",
+ [{datatype, string}]}.
+
+{mapping, "prometheus.ssl.verify", "rabbitmq_prometheus.ssl_config.verify", [
+ {datatype, {enum, [verify_peer, verify_none]}}]}.
+
+{mapping, "prometheus.ssl.fail_if_no_peer_cert", "rabbitmq_prometheus.ssl_config.fail_if_no_peer_cert", [
+ {datatype, {enum, [true, false]}}]}.
+
+{mapping, "prometheus.ssl.honor_cipher_order", "rabbitmq_prometheus.ssl_config.honor_cipher_order",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "prometheus.ssl.honor_ecc_order", "rabbitmq_prometheus.ssl_config.honor_ecc_order",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "prometheus.ssl.reuse_sessions", "rabbitmq_prometheus.ssl_config.reuse_sessions",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "prometheus.ssl.secure_renegotiate", "rabbitmq_prometheus.ssl_config.secure_renegotiate",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "prometheus.ssl.client_renegotiation", "rabbitmq_prometheus.ssl_config.client_renegotiation",
+ [{datatype, {enum, [true, false]}}]}.
+
+{mapping, "prometheus.ssl.depth", "rabbitmq_prometheus.ssl_config.depth",
+ [{datatype, integer}, {validators, ["byte"]}]}.
+
+{mapping, "prometheus.ssl.versions.$version", "rabbitmq_prometheus.ssl_config.versions",
+ [{datatype, atom}]}.
+
+{translation, "rabbitmq_prometheus.ssl_config.versions",
+fun(Conf) ->
+ Settings = cuttlefish_variable:filter_by_prefix("prometheus.ssl.versions", Conf),
+ [V || {_, V} <- Settings]
+end}.
+
+{mapping, "prometheus.ssl.ciphers.$cipher", "rabbitmq_prometheus.ssl_config.ciphers",
+ [{datatype, string}]}.
+
+{translation, "rabbitmq_prometheus.ssl_config.ciphers",
+fun(Conf) ->
+ Settings = cuttlefish_variable:filter_by_prefix("prometheus.ssl.ciphers", Conf),
+ lists:reverse([V || {_, V} <- Settings])
+end}.
+
+{mapping, "prometheus.ssl.compress", "rabbitmq_prometheus.ssl_config.cowboy_opts.compress",
+ [{datatype, {enum, [true, false]}}]}.
+{mapping, "prometheus.ssl.idle_timeout", "rabbitmq_prometheus.ssl_config.cowboy_opts.idle_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "prometheus.ssl.inactivity_timeout", "rabbitmq_prometheus.ssl_config.cowboy_opts.inactivity_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "prometheus.ssl.request_timeout", "rabbitmq_prometheus.ssl_config.cowboy_opts.request_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "prometheus.ssl.shutdown_timeout", "rabbitmq_prometheus.ssl_config.cowboy_opts.shutdown_timeout",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
+{mapping, "prometheus.ssl.max_keepalive", "rabbitmq_prometheus.ssl_config.cowboy_opts.max_keepalive",
+ [{datatype, integer}, {validators, ["non_negative_integer"]}]}.
diff --git a/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl b/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl
new file mode 100644
index 0000000000..9ef4a43efa
--- /dev/null
+++ b/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl
@@ -0,0 +1,282 @@
+%% This Source Code Form is subject to the terms of the Mozilla Public
+%% License, v. 2.0. If a copy of the MPL was not distributed with this
+%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
+%%
+%% Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(rabbit_prometheus_http_SUITE).
+
+-include_lib("amqp_client/include/amqp_client.hrl").
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, default_config},
+ {group, config_path},
+ {group, config_port},
+ {group, aggregated_metrics},
+ {group, per_object_metrics},
+ {group, commercial}
+ ].
+
+groups() ->
+ [
+ {default_config, [], generic_tests()},
+ {config_path, [], generic_tests()},
+ {config_port, [], generic_tests()},
+ {aggregated_metrics, [], [
+ aggregated_metrics_test,
+ specific_erlang_metrics_present_test
+ ]},
+ {per_object_metrics, [], [
+ per_object_metrics_test,
+ specific_erlang_metrics_present_test
+ ]},
+ {commercial, [], [
+ build_info_product_test
+ ]}
+ ].
+
+generic_tests() ->
+ [
+ get_test,
+ content_type_test,
+ encoding_test,
+ gzip_encoding_test,
+ build_info_test,
+ identity_info_test
+ ].
+
+%% -------------------------------------------------------------------
+%% Testsuite setup/teardown.
+%% -------------------------------------------------------------------
+init_per_group(default_config, Config) ->
+ init_per_group(default_config, Config, []);
+init_per_group(config_path, Config0) ->
+ PathConfig = {rabbitmq_prometheus, [{path, "/bunnieshop"}]},
+ Config1 = rabbit_ct_helpers:merge_app_env(Config0, PathConfig),
+ init_per_group(config_path, Config1, [{prometheus_path, "/bunnieshop"}]);
+init_per_group(config_port, Config0) ->
+ PathConfig = {rabbitmq_prometheus, [{tcp_config, [{port, 15772}]}]},
+ Config1 = rabbit_ct_helpers:merge_app_env(Config0, PathConfig),
+ init_per_group(config_port, Config1, [{prometheus_port, 15772}]);
+init_per_group(per_object_metrics, Config0) ->
+ PathConfig = {rabbitmq_prometheus, [{return_per_object_metrics, true}]},
+ Config1 = rabbit_ct_helpers:merge_app_env(Config0, PathConfig),
+ init_per_group(aggregated_metrics, Config1);
+init_per_group(aggregated_metrics, Config0) ->
+ Config1 = rabbit_ct_helpers:merge_app_env(
+ Config0,
+ [{rabbit, [{collect_statistics, coarse}, {collect_statistics_interval, 100}]}]
+ ),
+ Config2 = init_per_group(aggregated_metrics, Config1, []),
+ ok = rabbit_ct_broker_helpers:enable_feature_flag(Config2, quorum_queue),
+
+ A = rabbit_ct_broker_helpers:get_node_config(Config2, 0, nodename),
+ Ch = rabbit_ct_client_helpers:open_channel(Config2, A),
+
+ Q = <<"prometheus_test_queue">>,
+ amqp_channel:call(Ch,
+ #'queue.declare'{queue = Q,
+ durable = true,
+ arguments = [{<<"x-queue-type">>, longstr, <<"quorum">>}]
+ }),
+ amqp_channel:cast(Ch,
+ #'basic.publish'{routing_key = Q},
+ #amqp_msg{payload = <<"msg">>}),
+ timer:sleep(150),
+ {#'basic.get_ok'{}, #amqp_msg{}} = amqp_channel:call(Ch, #'basic.get'{queue = Q}),
+ %% We want to check consumer metrics, so we need at least 1 consumer bound
+ %% but we don't care what it does if anything as long as the runner process does
+ %% not have to handle the consumer's messages.
+ ConsumerPid = sleeping_consumer(),
+ #'basic.consume_ok'{consumer_tag = CTag} =
+ amqp_channel:subscribe(Ch, #'basic.consume'{queue = Q}, ConsumerPid),
+ timer:sleep(10000),
+
+ Config2 ++ [{channel_pid, Ch}, {queue_name, Q}, {consumer_tag, CTag}, {consumer_pid, ConsumerPid}];
+init_per_group(commercial, Config0) ->
+ ProductConfig = {rabbit, [{product_name, "WolfMQ"}, {product_version, "2020"}]},
+ Config1 = rabbit_ct_helpers:merge_app_env(Config0, ProductConfig),
+ init_per_group(commercial, Config1, []).
+
+init_per_group(Group, Config0, Extra) ->
+ rabbit_ct_helpers:log_environment(),
+ inets:start(),
+ NodeConf = [{rmq_nodename_suffix, Group}] ++ Extra,
+ Config1 = rabbit_ct_helpers:set_config(Config0, NodeConf),
+ rabbit_ct_helpers:run_setup_steps(Config1, rabbit_ct_broker_helpers:setup_steps()
+ ++ rabbit_ct_client_helpers:setup_steps()).
+
+end_per_group(aggregated_metrics, Config) ->
+ Ch = ?config(channel_pid, Config),
+ CTag = ?config(consumer_tag, Config),
+ amqp_channel:call(Ch, #'basic.cancel'{consumer_tag = CTag}),
+ ConsumerPid = ?config(consumer_pid, Config),
+ ConsumerPid ! stop,
+ amqp_channel:call(Ch, #'queue.delete'{queue = ?config(queue_name, Config)}),
+ rabbit_ct_client_helpers:close_channel(Ch),
+ end_per_group_(Config);
+end_per_group(_, Config) ->
+ end_per_group_(Config).
+
+end_per_group_(Config) ->
+ inets:stop(),
+ rabbit_ct_helpers:run_teardown_steps(Config, rabbit_ct_client_helpers:teardown_steps()
+ ++ rabbit_ct_broker_helpers:teardown_steps()).
+
+init_per_testcase(Testcase, Config) ->
+ rabbit_ct_helpers:testcase_started(Config, Testcase).
+
+end_per_testcase(Testcase, Config) ->
+ rabbit_ct_helpers:testcase_finished(Config, Testcase).
+
+%% a no-op consumer
+sleeping_consumer_loop() ->
+ receive
+ stop -> ok;
+ #'basic.consume_ok'{} -> sleeping_consumer_loop();
+ #'basic.cancel'{} -> sleeping_consumer_loop();
+ {#'basic.deliver'{}, _Payload} -> sleeping_consumer_loop()
+ end.
+
+sleeping_consumer() ->
+ spawn(fun() ->
+ sleeping_consumer_loop()
+ end).
+
+%% -------------------------------------------------------------------
+%% Testcases.
+%% -------------------------------------------------------------------
+
+get_test(Config) ->
+ {_Headers, Body} = http_get_with_pal(Config, [], 200),
+ %% Check that the body looks like a valid response
+ ?assertEqual(match, re:run(Body, "TYPE", [{capture, none}])),
+ Port = proplists:get_value(prometheus_port, Config, 15692),
+ URI = lists:flatten(io_lib:format("http://localhost:~p/metricsooops", [Port])),
+ {ok, {{_, CodeAct, _}, _, _}} = httpc:request(get, {URI, []}, ?HTTPC_OPTS, []),
+ ?assertMatch(404, CodeAct).
+
+content_type_test(Config) ->
+ {Headers, Body} = http_get_with_pal(Config, [{"accept", "text/plain"}], 200),
+ ?assertEqual(match, re:run(proplists:get_value("content-type", Headers),
+ "text/plain", [{capture, none}])),
+ %% Check that the body looks like a valid response
+ ?assertEqual(match, re:run(Body, "^# TYPE", [{capture, none}, multiline])),
+
+ http_get_with_pal(Config, [{"accept", "text/plain, text/html"}], 200),
+ http_get_with_pal(Config, [{"accept", "*/*"}], 200),
+ http_get_with_pal(Config, [{"accept", "text/xdvi"}], 406),
+ http_get_with_pal(Config, [{"accept", "application/vnd.google.protobuf"}], 406).
+
+encoding_test(Config) ->
+ {Headers, Body} = http_get(Config, [{"accept-encoding", "deflate"}], 200),
+ ?assertMatch("identity", proplists:get_value("content-encoding", Headers)),
+ ?assertEqual(match, re:run(Body, "^# TYPE", [{capture, none}, multiline])).
+
+gzip_encoding_test(Config) ->
+ {Headers, Body} = http_get(Config, [{"accept-encoding", "gzip"}], 200),
+ ?assertMatch("gzip", proplists:get_value("content-encoding", Headers)),
+ %% If the body is not gzip, zlib:gunzip will crash
+ ?assertEqual(match, re:run(zlib:gunzip(Body), "^# TYPE", [{capture, none}, multiline])).
+
+aggregated_metrics_test(Config) ->
+ {_Headers, Body} = http_get_with_pal(Config, [], 200),
+ ?assertEqual(match, re:run(Body, "^# TYPE", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^# HELP", [{capture, none}, multiline])),
+ ?assertEqual(nomatch, re:run(Body, ?config(queue_name, Config), [{capture, none}])),
+ %% Check the first metric value from each ETS table owned by rabbitmq_metrics
+ ?assertEqual(match, re:run(Body, "^rabbitmq_channel_consumers ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_channel_messages_published_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_channel_process_reductions_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_channel_get_ack_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_connections_opened_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_connection_incoming_bytes_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_connection_incoming_packets_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_queue_messages_published_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_process_open_fds ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_process_max_fds ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_io_read_ops_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_raft_term_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_queue_messages_ready ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_queue_consumers ", [{capture, none}, multiline])),
+ %% Check the first metric value in each ETS table that requires converting
+ ?assertEqual(match, re:run(Body, "^rabbitmq_erlang_uptime_seconds ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_io_read_time_seconds_total ", [{capture, none}, multiline])),
+ %% Check the first TOTALS metric value
+ ?assertEqual(match, re:run(Body, "^rabbitmq_connections ", [{capture, none}, multiline])),
+ %% Check raft_entry_commit_latency_seconds because we are aggregating it
+ ?assertEqual(match, re:run(Body, "^rabbitmq_raft_entry_commit_latency_seconds ", [{capture, none}, multiline])).
+
+per_object_metrics_test(Config) ->
+ {_Headers, Body} = http_get_with_pal(Config, [], 200),
+ ?assertEqual(match, re:run(Body, "^# TYPE", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^# HELP", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, ?config(queue_name, Config), [{capture, none}])),
+ %% Check the first metric value from each ETS table owned by rabbitmq_metrics
+ ?assertEqual(match, re:run(Body, "^rabbitmq_channel_consumers{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_channel_messages_published_total{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_channel_process_reductions_total{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_channel_get_ack_total{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_connections_opened_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_connection_incoming_bytes_total{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_connection_incoming_packets_total{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_queue_messages_published_total{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_process_open_fds ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_process_max_fds ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_io_read_ops_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_raft_term_total{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_queue_messages_ready{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_queue_consumers{", [{capture, none}, multiline])),
+ %% Check the first metric value in each ETS table that requires converting
+ ?assertEqual(match, re:run(Body, "^rabbitmq_erlang_uptime_seconds ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_io_read_time_seconds_total ", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_raft_entry_commit_latency_seconds{", [{capture, none}, multiline])),
+ %% Check the first TOTALS metric value
+ ?assertEqual(match, re:run(Body, "^rabbitmq_connections ", [{capture, none}, multiline])).
+
+build_info_test(Config) ->
+ {_Headers, Body} = http_get_with_pal(Config, [], 200),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_build_info{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "rabbitmq_version=\"", [{capture, none}])),
+ ?assertEqual(match, re:run(Body, "prometheus_plugin_version=\"", [{capture, none}])),
+ ?assertEqual(match, re:run(Body, "prometheus_client_version=\"", [{capture, none}])),
+ ?assertEqual(match, re:run(Body, "erlang_version=\"", [{capture, none}])).
+
+build_info_product_test(Config) ->
+ {_Headers, Body} = http_get_with_pal(Config, [], 200),
+ ?assertEqual(match, re:run(Body, "product_name=\"WolfMQ\"", [{capture, none}])),
+ ?assertEqual(match, re:run(Body, "product_version=\"2020\"", [{capture, none}])),
+ %% Check that RabbitMQ version is still displayed
+ ?assertEqual(match, re:run(Body, "rabbitmq_version=\"", [{capture, none}])).
+
+identity_info_test(Config) ->
+ {_Headers, Body} = http_get_with_pal(Config, [], 200),
+ ?assertEqual(match, re:run(Body, "^rabbitmq_identity_info{", [{capture, none}, multiline])),
+ ?assertEqual(match, re:run(Body, "rabbitmq_node=", [{capture, none}])),
+ ?assertEqual(match, re:run(Body, "rabbitmq_cluster=", [{capture, none}])).
+
+specific_erlang_metrics_present_test(Config) ->
+ {_Headers, Body} = http_get_with_pal(Config, [], 200),
+ ?assertEqual(match, re:run(Body, "^erlang_vm_dist_node_queue_size_bytes{", [{capture, none}, multiline])).
+
+http_get(Config, ReqHeaders, CodeExp) ->
+ Path = proplists:get_value(prometheus_path, Config, "/metrics"),
+ Port = proplists:get_value(prometheus_port, Config, 15692),
+ URI = lists:flatten(io_lib:format("http://localhost:~p~s", [Port, Path])),
+ {ok, {{_HTTP, CodeAct, _}, Headers, Body}} =
+ httpc:request(get, {URI, ReqHeaders}, ?HTTPC_OPTS, []),
+ ?assertMatch(CodeExp, CodeAct),
+ {Headers, Body}.
+
+http_get_with_pal(Config, ReqHeaders, CodeExp) ->
+ {Headers, Body} = http_get(Config, ReqHeaders, CodeExp),
+ %% Print and log response body - it makes is easier to find why a match failed
+ ct:pal(Body),
+ {Headers, Body}.