diff options
Diffstat (limited to 'deps/rabbitmq_prometheus/test')
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}. |