Asterisk - The Open Source Telephony Project GIT-master-7e7a603
test_res_prometheus.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2019 Sangoma, Inc.
5 *
6 * Matt Jordan <mjordan@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <depend>TEST_FRAMEWORK</depend>
21 <depend>res_prometheus</depend>
22 <depend>curl</depend>
23 <support_level>extended</support_level>
24 ***/
25
26#include "asterisk.h"
27
28#include <curl/curl.h>
29
30#include "asterisk/test.h"
31#include "asterisk/module.h"
32#include "asterisk/bridge.h"
34#include "asterisk/config.h"
36#include "../res/prometheus/prometheus_internal.h"
37
38#define CATEGORY "/res/prometheus/"
39
40static char server_uri[512];
41
43
44static void curl_free_wrapper(void *ptr)
45{
46 if (!ptr) {
47 return;
48 }
49
50 curl_easy_cleanup(ptr);
51}
52
53static void prometheus_metric_free_wrapper(void *ptr)
54{
57 }
58}
59
61{
63
65 if (!config) {
66 return NULL;
67 }
68
69 /* Set what we need on the config for most tests */
70 ast_string_field_set(config, uri, "test_metrics");
71 config->enabled = 1;
72 config->core_metrics_enabled = 0;
73
74 return config;
75}
76
77static CURL *get_curl_instance(void)
78{
79 CURL *curl;
80
81 curl = curl_easy_init();
82 if (!curl) {
83 return NULL;
84 }
85
86 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
87 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180);
88 curl_easy_setopt(curl, CURLOPT_USERAGENT, AST_CURL_USER_AGENT);
89 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
90 curl_easy_setopt(curl, CURLOPT_URL, server_uri);
91
92 return curl;
93}
94
95static size_t curl_write_string_callback(void *contents, size_t size, size_t nmemb, void *userdata)
96{
97 struct ast_str **buffer = userdata;
98 size_t realsize = size * nmemb;
99 char *rawdata;
100
101 rawdata = ast_malloc(realsize + 1);
102 if (!rawdata) {
103 return 0;
104 }
105 memcpy(rawdata, contents, realsize);
106 rawdata[realsize] = 0;
107 ast_str_append(buffer, 0, "%s", rawdata);
108 ast_free(rawdata);
109
110 return realsize;
111}
112
114{
115 strcpy(metric->value, "2");
116}
117
118AST_TEST_DEFINE(metric_values)
119{
120 RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
121 RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
122 int res;
125 "test_counter_one",
126 "A test counter",
127 NULL);
130 "test_counter_two",
131 "A test counter",
134
135 switch (cmd) {
136 case TEST_INIT:
137 info->name = __func__;
138 info->category = CATEGORY;
139 info->summary = "Test value generation/respecting in metrics";
140 info->description =
141 "Metrics have two ways to provide values when the HTTP callback\n"
142 "is invoked:\n"
143 "1. By using the direct value that resides in the metric\n"
144 "2. By providing a callback function to specify the value\n"
145 "This test verifies that both function appropriately when the\n"
146 "HTTP callback is called.";
147 return AST_TEST_NOT_RUN;
148 case TEST_EXECUTE:
149 break;
150 }
151
152 buffer = ast_str_create(128);
153 if (!buffer) {
154 return AST_TEST_FAIL;
155 }
156
157 curl = get_curl_instance();
158 if (!curl) {
159 return AST_TEST_FAIL;
160 }
161
162 ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter_one) == 0, result, metric_values_cleanup);
163 ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter_two) == 0, result, metric_values_cleanup);
164 strcpy(test_counter_one.value, "1");
165
166 ast_test_status_update(test, " -> CURLing request...\n");
167 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);
168 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
169 res = curl_easy_perform(curl);
170 if (res != CURLE_OK) {
171 ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
173 goto metric_values_cleanup;
174 }
175
176 ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));
177 ast_test_validate_cleanup(test, strstr(ast_str_buffer(buffer),
178 "# HELP test_counter_one A test counter\n"
179 "# TYPE test_counter_one counter\n"
180 "test_counter_one 1\n"
181 "# HELP test_counter_two A test counter\n"
182 "# TYPE test_counter_two counter\n"
183 "test_counter_two 2\n") != NULL, result, metric_values_cleanup);
184
185metric_values_cleanup:
186 prometheus_metric_unregister(&test_counter_one);
187 prometheus_metric_unregister(&test_counter_two);
188
189 return result;
190}
191
192static void prometheus_metric_callback(struct ast_str **output)
193{
196 "test_counter",
197 "A test counter",
198 NULL);
199
200 prometheus_metric_to_string(&test_counter, output);
201}
202
203AST_TEST_DEFINE(metric_callback_register)
204{
205 RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
206 RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
207 int res;
208 struct prometheus_callback callback = {
209 .name = "test_callback",
210 .callback_fn = &prometheus_metric_callback,
211 };
212
213 switch (cmd) {
214 case TEST_INIT:
215 info->name = __func__;
216 info->category = CATEGORY;
217 info->summary = "Test registration of callbacks";
218 info->description =
219 "This test covers callback registration. It registers\n"
220 "a callback that is invoked when an HTTP request is made,\n"
221 "and it verifies that during said callback the output to\n"
222 "the response string is correctly appended to. It also verifies\n"
223 "that unregistered callbacks are not invoked.";
224 return AST_TEST_NOT_RUN;
225 case TEST_EXECUTE:
226 break;
227 }
228
229 buffer = ast_str_create(128);
230 if (!buffer) {
231 return AST_TEST_FAIL;
232 }
233
234 ast_test_validate(test, prometheus_callback_register(&callback) == 0);
235
236 curl = get_curl_instance();
237 if (!curl) {
238 return AST_TEST_NOT_RUN;
239 }
240
241 ast_test_status_update(test, " -> CURLing request...\n");
242 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);
243 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
244 res = curl_easy_perform(curl);
245 if (res != CURLE_OK) {
246 ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
247 return AST_TEST_FAIL;
248 }
249
250 ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));
251 ast_test_validate(test, strstr(ast_str_buffer(buffer),
252 "# HELP test_counter A test counter\n"
253 "# TYPE test_counter counter\n"
254 "test_counter 0\n") != NULL);
255
257
258 return AST_TEST_PASS;
259}
260
261AST_TEST_DEFINE(metric_register)
262{
265 "test_counter",
266 "A test counter",
267 NULL);
269 RAII_VAR(struct prometheus_metric *, test_gauge_child_one, NULL, prometheus_metric_free_wrapper);
270 RAII_VAR(struct prometheus_metric *, test_gauge_child_two, NULL, prometheus_metric_free_wrapper);
273
274 switch (cmd) {
275 case TEST_INIT:
276 info->name = __func__;
277 info->category = CATEGORY;
278 info->summary = "Test registration of metrics";
279 info->description =
280 "This test covers the following registration scenarios:\n"
281 "- Nominal registration of simple metrics\n"
282 "- Registration of metrics with different allocation strategies\n"
283 "- Nested metrics with label families\n"
284 "- Off nominal registration with simple name collisions\n"
285 "- Off nominal registration with label collisions";
286 return AST_TEST_NOT_RUN;
287 case TEST_EXECUTE:
288 break;
289 }
290
291 ast_test_status_update(test, "Testing nominal registration\n");
292 ast_test_status_update(test, "-> Static metric\n");
293 ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter) == 0, result, metric_register_cleanup);
294 ast_test_status_update(test, "-> Malloc'd metric\n");
295 test_gauge = prometheus_gauge_create("test_gauge", "A test gauge");
296 ast_test_validate(test, test_gauge != NULL);
297 ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge) == 0, result, metric_register_cleanup);
298 ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);
299
300 ast_test_status_update(test, "Testing nominal registration of child metrics\n");
301 test_gauge_child_one = prometheus_gauge_create("test_gauge", "A test gauge");
302 ast_test_validate_cleanup(test, test_gauge_child_one != NULL, result, metric_register_cleanup);
303 PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_one, 0, "key_one", "value_one");
304 PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_one, 1, "key_two", "value_one");
305 test_gauge_child_two = prometheus_gauge_create("test_gauge", "A test gauge");
306 ast_test_validate_cleanup(test, test_gauge_child_two != NULL, result, metric_register_cleanup);
307 PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_two, 0, "key_one", "value_two");
308 PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_two, 1, "key_two", "value_two");
309 ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge_child_one) == 0, result, metric_register_cleanup);
310 ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge_child_two) == 0, result, metric_register_cleanup);
311 ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);
312 ast_test_validate_cleanup(test, test_gauge->children.first == test_gauge_child_one, result, metric_register_cleanup);
313 ast_test_validate_cleanup(test, test_gauge->children.last == test_gauge_child_two, result, metric_register_cleanup);
314
315 ast_test_status_update(test, "Testing name collisions\n");
316 bad_metric = prometheus_counter_create("test_counter", "A test counter");
317 ast_test_validate_cleanup(test, bad_metric != NULL, result, metric_register_cleanup);
318 ast_test_validate_cleanup(test, prometheus_metric_register(bad_metric) != 0, result, metric_register_cleanup);
319 prometheus_metric_free(bad_metric);
320 bad_metric = NULL;
321
322 ast_test_status_update(test, "Testing label collisions\n");
323 bad_metric = prometheus_gauge_create("test_gauge", "A test gauge");
324 ast_test_validate_cleanup(test, bad_metric != NULL, result, metric_register_cleanup);
325 PROMETHEUS_METRIC_SET_LABEL(bad_metric, 0, "key_one", "value_one");
326 PROMETHEUS_METRIC_SET_LABEL(bad_metric, 1, "key_two", "value_one");
327 ast_test_validate_cleanup(test, prometheus_metric_register(bad_metric) != 0, result, metric_register_cleanup);
328 prometheus_metric_free(bad_metric);
329 bad_metric = NULL;
330
331 ast_test_status_update(test, "Testing removal of metrics\n");
332 prometheus_metric_unregister(test_gauge_child_two);
333 test_gauge_child_two = NULL;
334
335 ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);
337 test_gauge = NULL;
338
339 ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);
340 prometheus_metric_unregister(test_gauge_child_one);
341 test_gauge_child_one = NULL;
342
343 ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 1, result, metric_register_cleanup);
344 prometheus_metric_unregister(&test_counter);
345
346 ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 0, result, metric_register_cleanup);
347
348 return AST_TEST_PASS;
349
350metric_register_cleanup:
351 prometheus_metric_unregister(&test_counter);
352 return result;
353}
354
355AST_TEST_DEFINE(counter_to_string)
356{
359 "test_counter",
360 "A test counter",
361 NULL);
362 struct prometheus_metric test_counter_child_one = PROMETHEUS_METRIC_STATIC_INITIALIZATION(
364 "test_counter",
365 "A test counter",
366 NULL);
367 struct prometheus_metric test_counter_child_two = PROMETHEUS_METRIC_STATIC_INITIALIZATION(
369 "test_counter",
370 "A test counter",
371 NULL);
372 RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
373
374 switch (cmd) {
375 case TEST_INIT:
376 info->name = __func__;
377 info->category = CATEGORY;
378 info->summary = "Test formatting of counters";
379 info->description =
380 "This test covers the formatting of printed counters";
381 return AST_TEST_NOT_RUN;
382 case TEST_EXECUTE:
383 break;
384 }
385
386 buffer = ast_str_create(128);
387 if (!buffer) {
388 return AST_TEST_FAIL;
389 }
390
391 PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_one, 0, "key_one", "value_one");
392 PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_one, 1, "key_two", "value_one");
393 PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_two, 0, "key_one", "value_two");
394 PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_two, 1, "key_two", "value_two");
395 AST_LIST_INSERT_TAIL(&test_counter.children, &test_counter_child_one, entry);
396 AST_LIST_INSERT_TAIL(&test_counter.children, &test_counter_child_two, entry);
397 prometheus_metric_to_string(&test_counter, &buffer);
398 ast_test_validate(test, strcmp(ast_str_buffer(buffer),
399 "# HELP test_counter A test counter\n"
400 "# TYPE test_counter counter\n"
401 "test_counter 0\n"
402 "test_counter{key_one=\"value_one\",key_two=\"value_one\"} 0\n"
403 "test_counter{key_one=\"value_two\",key_two=\"value_two\"} 0\n") == 0);
404
405 return AST_TEST_PASS;
406}
407
408AST_TEST_DEFINE(counter_create)
409{
411
412 switch (cmd) {
413 case TEST_INIT:
414 info->name = __func__;
415 info->category = CATEGORY;
416 info->summary = "Test creation (and destruction) of malloc'd counters";
417 info->description =
418 "This test covers creating a counter metric and destroying\n"
419 "it. The metric should be malloc'd.";
420 return AST_TEST_NOT_RUN;
421 case TEST_EXECUTE:
422 break;
423 }
424
425 metric = prometheus_counter_create("test_counter", "A test counter");
426 ast_test_validate(test, metric != NULL);
427 ast_test_validate(test, metric->type == PROMETHEUS_METRIC_COUNTER);
428 ast_test_validate(test, metric->allocation_strategy = PROMETHEUS_METRIC_MALLOCD);
429 ast_test_validate(test, !strcmp(metric->help, "A test counter"));
430 ast_test_validate(test, !strcmp(metric->name, "test_counter"));
431 ast_test_validate(test, !strcmp(metric->value, ""));
432 ast_test_validate(test, metric->children.first == NULL);
433 ast_test_validate(test, metric->children.last == NULL);
434
435 return AST_TEST_PASS;
436}
437
438AST_TEST_DEFINE(gauge_to_string)
439{
442 "test_gauge",
443 "A test gauge",
444 NULL);
447 "test_gauge",
448 "A test gauge",
449 NULL);
452 "test_gauge",
453 "A test gauge",
454 NULL);
455 RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
456
457 switch (cmd) {
458 case TEST_INIT:
459 info->name = __func__;
460 info->category = CATEGORY;
461 info->summary = "Test formatting of gauges";
462 info->description =
463 "This test covers the formatting of printed gauges";
464 return AST_TEST_NOT_RUN;
465 case TEST_EXECUTE:
466 break;
467 }
468
469 buffer = ast_str_create(128);
470 if (!buffer) {
471 return AST_TEST_FAIL;
472 }
473
474 PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_one, 0, "key_one", "value_one");
475 PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_one, 1, "key_two", "value_one");
476 PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_two, 0, "key_one", "value_two");
477 PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_two, 1, "key_two", "value_two");
478 AST_LIST_INSERT_TAIL(&test_gauge.children, &test_gauge_child_one, entry);
479 AST_LIST_INSERT_TAIL(&test_gauge.children, &test_gauge_child_two, entry);
480 prometheus_metric_to_string(&test_gauge, &buffer);
481 ast_test_validate(test, strcmp(ast_str_buffer(buffer),
482 "# HELP test_gauge A test gauge\n"
483 "# TYPE test_gauge gauge\n"
484 "test_gauge 0\n"
485 "test_gauge{key_one=\"value_one\",key_two=\"value_one\"} 0\n"
486 "test_gauge{key_one=\"value_two\",key_two=\"value_two\"} 0\n") == 0);
487
488 return AST_TEST_PASS;
489}
490
491AST_TEST_DEFINE(gauge_create)
492{
494
495 switch (cmd) {
496 case TEST_INIT:
497 info->name = __func__;
498 info->category = CATEGORY;
499 info->summary = "Test creation (and destruction) of malloc'd gauges";
500 info->description =
501 "This test covers creating a gauge metric and destroying\n"
502 "it. The metric should be malloc'd.";
503 return AST_TEST_NOT_RUN;
504 case TEST_EXECUTE:
505 break;
506 }
507
508 metric = prometheus_gauge_create("test_gauge", "A test gauge");
509 ast_test_validate(test, metric != NULL);
510 ast_test_validate(test, metric->type == PROMETHEUS_METRIC_GAUGE);
511 ast_test_validate(test, metric->allocation_strategy = PROMETHEUS_METRIC_MALLOCD);
512 ast_test_validate(test, !strcmp(metric->help, "A test gauge"));
513 ast_test_validate(test, !strcmp(metric->name, "test_gauge"));
514 ast_test_validate(test, !strcmp(metric->value, ""));
515 ast_test_validate(test, metric->children.first == NULL);
516 ast_test_validate(test, metric->children.last == NULL);
517
518 return AST_TEST_PASS;
519}
520
521AST_TEST_DEFINE(config_general_basic_auth)
522{
523 RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
525 int res;
526 long response_code;
527
528 switch (cmd) {
529 case TEST_INIT:
530 info->name = __func__;
531 info->category = CATEGORY;
532 info->summary = "Test basic auth handling";
533 info->description =
534 "This test covers authentication of requests";
535 return AST_TEST_NOT_RUN;
536 case TEST_EXECUTE:
537 break;
538 }
539
541 if (!config) {
542 return AST_TEST_NOT_RUN;
543 }
546 /* Prometheus module owns the ref after this call */
548 ao2_ref(config, -1);
549
550 curl = get_curl_instance();
551 if (!curl) {
552 return AST_TEST_NOT_RUN;
553 }
554
555 ast_test_status_update(test, "Testing without auth credentials\n");
556 ast_test_status_update(test, " -> CURLing request...\n");
557 res = curl_easy_perform(curl);
558 if (res != CURLE_OK) {
559 ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
560 return AST_TEST_FAIL;
561 }
562 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
563 ast_test_status_update(test, " -> CURL returned %ld\n", response_code);
564 ast_test_validate(test, response_code == 401);
565
566 ast_test_status_update(test, "Testing with invalid auth credentials\n");
567 ast_test_status_update(test, " -> CURLing request...\n");
568 curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
569 curl_easy_setopt(curl, CURLOPT_USERPWD, "matt:jordan");
570 res = curl_easy_perform(curl);
571 if (res != CURLE_OK) {
572 ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
573 return AST_TEST_FAIL;
574 }
575 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
576 ast_test_status_update(test, " -> CURL returned %ld\n", response_code);
577 ast_test_validate(test, response_code == 401);
578
579 ast_test_status_update(test, "Testing with valid auth credentials\n");
580 ast_test_status_update(test, " -> CURLing request...\n");
581 curl_easy_setopt(curl, CURLOPT_USERPWD, "foo:bar");
582 res = curl_easy_perform(curl);
583 if (res != CURLE_OK) {
584 ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
585 return AST_TEST_FAIL;
586 }
587 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
588 ast_test_status_update(test, " -> CURL returned %ld\n", response_code);
589 ast_test_validate(test, response_code == 200);
590
591 return AST_TEST_PASS;
592}
593
594AST_TEST_DEFINE(config_general_enabled)
595{
596 RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
598 int res;
599 long response_code;
600
601 switch (cmd) {
602 case TEST_INIT:
603 info->name = __func__;
604 info->category = CATEGORY;
605 info->summary = "Test handling of enable/disable";
606 info->description =
607 "When disabled, the module should return a 503.\n"
608 "This test verifies that it actually occurs.";
609 return AST_TEST_NOT_RUN;
610 case TEST_EXECUTE:
611 break;
612 }
613
615 if (!config) {
616 return AST_TEST_NOT_RUN;
617 }
618 config->enabled = 0;
619 /* Prometheus module owns the ref after this call */
621 ao2_ref(config, -1);
622
623 curl = get_curl_instance();
624 if (!curl) {
625 return AST_TEST_NOT_RUN;
626 }
627
628 ast_test_status_update(test, " -> CURLing request...\n");
629 res = curl_easy_perform(curl);
630 if (res != CURLE_OK) {
631 ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
632 return AST_TEST_FAIL;
633 }
634 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
635 ast_test_status_update(test, " -> CURL returned %ld\n", response_code);
636 ast_test_validate(test, response_code == 503);
637
638 return AST_TEST_PASS;
639}
640
641AST_TEST_DEFINE(config_general_core_metrics)
642{
643 RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
644 RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
646 int res;
647
648 switch (cmd) {
649 case TEST_INIT:
650 info->name = __func__;
651 info->category = CATEGORY;
652 info->summary = "Test producing core metrics";
653 info->description =
654 "This test covers the core metrics that are produced\n"
655 "by the basic Prometheus module.";
656 return AST_TEST_NOT_RUN;
657 case TEST_EXECUTE:
658 break;
659 }
660
661 buffer = ast_str_create(128);
662 if (!buffer) {
663 return AST_TEST_NOT_RUN;
664 }
665
667 if (!config) {
668 return AST_TEST_NOT_RUN;
669 }
670 config->core_metrics_enabled = 1;
671 /* Prometheus module owns the ref after this call */
673 ao2_ref(config, -1);
674
675 curl = get_curl_instance();
676 if (!curl) {
677 return AST_TEST_NOT_RUN;
678 }
679
680 ast_test_status_update(test, " -> CURLing request...\n");
681 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);
682 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
683 res = curl_easy_perform(curl);
684 if (res != CURLE_OK) {
685 ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
686 return AST_TEST_FAIL;
687 }
688 ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));
689
690 ast_test_status_update(test, " -> Checking for core properties\n");
691 ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_properties") != NULL);
692
693 ast_test_status_update(test, " -> Checking for uptime\n");
694 ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_uptime_seconds") != NULL);
695
696 ast_test_status_update(test, " -> Checking for last reload\n");
697 ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_last_reload_seconds") != NULL);
698
699 ast_test_status_update(test, " -> Checking for scrape time\n");
700 ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_scrape_time_ms") != NULL);
701
702 return AST_TEST_PASS;
703}
704
705static void safe_bridge_destroy(struct ast_bridge *bridge)
706{
707 if (!bridge) {
708 return;
709 }
710 ast_bridge_destroy(bridge, 0);
711}
712
713static int match_count(const char *str, const char *needle)
714{
715 int count = 0;
716
717 while ((str = strstr(str, needle))) {
718 str += strlen(needle);
719 count++;
720 }
721
722 return count;
723}
724
725AST_TEST_DEFINE(bridge_to_string)
726{
727 RAII_VAR(struct ast_bridge *, bridge1, NULL, safe_bridge_destroy);
728 RAII_VAR(struct ast_bridge *, bridge2, NULL, safe_bridge_destroy);
729 RAII_VAR(struct ast_bridge *, bridge3, NULL, safe_bridge_destroy);
730 struct ast_str *response;
731
732 switch (cmd) {
733 case TEST_INIT:
734 info->name = __func__;
735 info->category = CATEGORY;
736 info->summary = "Test producing bridge metrics";
737 info->description =
738 "This test covers checking the metrics produced by the\n"
739 "bridge support of the basic Promtheus module.";
740 return AST_TEST_NOT_RUN;
741 case TEST_EXECUTE:
742 break;
743 }
744
745 bridge1 = ast_bridge_basic_new();
746 ast_test_validate(test, bridge1 != NULL);
747
750 "test_res_prometheus", "test_bridge_invisible", NULL);
751
752 bridge3 = ast_bridge_basic_new();
753 ast_test_validate(test, bridge3 != NULL);
754
755 response = prometheus_scrape_to_string();
756 if (!response) {
757 return AST_TEST_FAIL;
758 }
759
760 ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(response));
761 ast_test_validate(test, strstr(ast_str_buffer(response), "(null)") == NULL);
762 ast_test_validate(test, strstr(ast_str_buffer(response), "asterisk_bridges_channels_count{") != NULL);
763 ast_test_validate(test, match_count(ast_str_buffer(response), "# HELP asterisk_bridges_channels_count Number of channels in the bridge.") == 1);
764 ast_free(response);
765 return AST_TEST_PASS;
766}
767
768static int process_config(int reload)
769{
770 struct ast_config *config;
771 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
772 const char *bindaddr;
773 const char *bindport;
774 const char *prefix;
775 const char *enabled;
776
777 config = ast_config_load("http.conf", config_flags);
779 ast_log(AST_LOG_NOTICE, "HTTP config file is invalid; declining load");
780 return -1;
781 } else if (config == CONFIG_STATUS_FILEUNCHANGED) {
782 return 0;
783 }
784
785 enabled = ast_config_option(config, "general", "enabled");
786 if (!enabled || ast_false(enabled)) {
788 ast_log(AST_LOG_NOTICE, "HTTP server is disabled; declining load");
789 return -1;
790 }
791
792 /* Construct our Server URI */
793 bindaddr = ast_config_option(config, "general", "bindaddr");
794 if (!bindaddr) {
796 ast_log(AST_LOG_NOTICE, "HTTP config file fails to specify 'bindaddr'; declining load");
797 return -1;
798 }
799
800 bindport = ast_config_option(config, "general", "bindport");
801 if (!bindport) {
802 bindport = "8088";
803 }
804
805 prefix = ast_config_option(config, "general", "prefix");
806
807 snprintf(server_uri, sizeof(server_uri), "http://%s:%s%s/test_metrics", bindaddr, bindport, S_OR(prefix, ""));
808
810
811 return 0;
812}
813
814static int test_init_cb(struct ast_test_info *info, struct ast_test *test)
815{
816 struct prometheus_general_config *new_module_config;
817
818 new_module_config = config_alloc();
819 if (!new_module_config) {
820 return -1;
821 }
822
824 prometheus_general_config_set(new_module_config);
825
826 /* Allow the module to own the ref */
827 ao2_ref(new_module_config, -1);
828
829 return 0;
830}
831
832static int test_cleanup_cb(struct ast_test_info *info, struct ast_test *test)
833{
836
837 return 0;
838}
839
840static int reload_module(void)
841{
842 return process_config(1);
843}
844
845static int unload_module(void)
846{
847 AST_TEST_UNREGISTER(metric_values);
848 AST_TEST_UNREGISTER(metric_callback_register);
849 AST_TEST_UNREGISTER(metric_register);
850
851 AST_TEST_UNREGISTER(counter_to_string);
852 AST_TEST_UNREGISTER(counter_create);
853 AST_TEST_UNREGISTER(gauge_to_string);
854 AST_TEST_UNREGISTER(gauge_create);
855
856 AST_TEST_UNREGISTER(config_general_enabled);
857 AST_TEST_UNREGISTER(config_general_basic_auth);
858 AST_TEST_UNREGISTER(config_general_core_metrics);
859
860 AST_TEST_UNREGISTER(bridge_to_string);
861
862 return 0;
863}
864
865static int load_module(void)
866{
867 if (process_config(0)) {
869 }
870
871 AST_TEST_REGISTER(metric_values);
872 AST_TEST_REGISTER(metric_callback_register);
873 AST_TEST_REGISTER(metric_register);
874
875 AST_TEST_REGISTER(counter_to_string);
876 AST_TEST_REGISTER(counter_create);
877 AST_TEST_REGISTER(gauge_to_string);
878 AST_TEST_REGISTER(gauge_create);
879
880 AST_TEST_REGISTER(config_general_enabled);
881 AST_TEST_REGISTER(config_general_basic_auth);
882 AST_TEST_REGISTER(config_general_core_metrics);
883
884 AST_TEST_REGISTER(bridge_to_string);
885
886 ast_test_register_init(CATEGORY, &test_init_cb);
887 ast_test_register_cleanup(CATEGORY, &test_cleanup_cb);
888
890}
891
892AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Prometheus Core Unit Tests",
893 .load = load_module,
895 .unload = unload_module,
896 .requires = "res_prometheus",
const char * str
Definition: app_jack.c:147
Asterisk main include file. File version handling, generic pbx functions.
#define AST_CURL_USER_AGENT
Definition: asterisk.h:44
#define ast_free(a)
Definition: astmm.h:180
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
Bridging API.
struct ast_bridge * ast_bridge_base_new(uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
Create a new base class bridge.
Definition: bridge.c:934
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:944
@ AST_BRIDGE_CAPABILITY_HOLDING
Definition: bridge.h:86
Basic bridge subclass API.
struct ast_bridge * ast_bridge_basic_new(void)
Create a new basic class bridge.
@ AST_BRIDGE_FLAG_INVISIBLE
static PGresult * result
Definition: cel_pgsql.c:84
struct ast_sockaddr bindaddr
Definition: chan_ooh323.c:353
static const char config[]
Definition: chan_ooh323.c:111
static int enabled
Definition: dnsmgr.c:91
static char prefix[MAX_PREFIX]
Definition: http.c:144
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: main/config.c:773
@ CONFIG_FLAG_FILEUNCHANGED
#define AST_LOG_NOTICE
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:315
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
def info(msg)
struct ast_str * prometheus_scrape_to_string(void)
Get the raw output of what a scrape would produce.
static int reload(void)
Asterisk Prometheus Metrics.
int prometheus_metric_unregister(struct prometheus_metric *metric)
Remove a registered metric.
#define PROMETHEUS_METRIC_SET_LABEL(metric, label, n, v)
Convenience macro for setting a label / value in a metric.
int prometheus_metric_register(struct prometheus_metric *metric)
int prometheus_metric_registered_count(void)
void prometheus_general_config_set(struct prometheus_general_config *config)
Set the configuration for the module.
#define PROMETHEUS_METRIC_STATIC_INITIALIZATION(mtype, n, h, cb)
Convenience macro for initializing a metric on the stack.
@ PROMETHEUS_METRIC_GAUGE
A metric whose value can bounce around like a jackrabbit.
@ PROMETHEUS_METRIC_COUNTER
A metric whose value always goes up.
struct prometheus_metric * prometheus_gauge_create(const char *name, const char *help)
Create a malloc'd gauge metric.
void prometheus_callback_unregister(struct prometheus_callback *callback)
Remove a registered callback.
void * prometheus_general_config_alloc(void)
Allocate a new configuration object.
void prometheus_metric_free(struct prometheus_metric *metric)
Destroy a metric and all its children.
void prometheus_metric_to_string(struct prometheus_metric *metric, struct ast_str **output)
Convert a metric (and its children) into Prometheus compatible text.
int prometheus_callback_register(struct prometheus_callback *callback)
struct prometheus_general_config * prometheus_general_config_get(void)
Retrieve the current configuration of the module.
@ PROMETHEUS_METRIC_MALLOCD
The metric was allocated on the heap.
struct prometheus_metric * prometheus_counter_create(const char *name, const char *help)
Create a malloc'd counter metric.
#define NULL
Definition: resample.c:96
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition: utils.c:2216
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
Structure that contains information about a bridge.
Definition: bridge.h:349
Structure used to handle boolean flags.
Definition: utils.h:199
Support for dynamic strings.
Definition: strings.h:623
Contains all the initialization information required to store a new test definition.
Definition: test.h:235
Definition: search.h:40
The configuration settings for this module.
Definition: cdr.c:264
Defines a callback that will be invoked when the HTTP route is called.
const char * name
The name of our callback (always useful for debugging)
Prometheus general configuration.
const ast_string_field uri
const ast_string_field auth_password
const ast_string_field auth_username
An actual, honest to god, metric.
struct prometheus_metric::@270 children
A list of children metrics.
char value[PROMETHEUS_MAX_VALUE_LENGTH]
The current value.
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
const char * contents
static void prometheus_metric_free_wrapper(void *ptr)
static int process_config(int reload)
static int match_count(const char *str, const char *needle)
static void metric_values_get_counter_value_cb(struct prometheus_metric *metric)
static void safe_bridge_destroy(struct ast_bridge *bridge)
AST_TEST_DEFINE(metric_values)
static CURL * get_curl_instance(void)
#define CATEGORY
static int test_init_cb(struct ast_test_info *info, struct ast_test *test)
static int reload_module(void)
static size_t curl_write_string_callback(void *contents, size_t size, size_t nmemb, void *userdata)
static void curl_free_wrapper(void *ptr)
static struct prometheus_general_config * config_alloc(void)
static int load_module(void)
struct prometheus_general_config * module_config
static void prometheus_metric_callback(struct ast_str **output)
static int unload_module(void)
static int test_cleanup_cb(struct ast_test_info *info, struct ast_test *test)
static char server_uri[512]
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941