Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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/config.h"
34 
35 #define CATEGORY "/res/prometheus/"
36 
37 static char server_uri[512];
38 
40 
41 static void curl_free_wrapper(void *ptr)
42 {
43  if (!ptr) {
44  return;
45  }
46 
47  curl_easy_cleanup(ptr);
48 }
49 
50 static void prometheus_metric_free_wrapper(void *ptr)
51 {
54  }
55 }
56 
57 #define GLOBAL_USERAGENT "asterisk-libcurl-agent/1.0"
58 
60 {
62 
64  if (!config) {
65  return NULL;
66  }
67 
68  /* Set what we need on the config for most tests */
69  ast_string_field_set(config, uri, "test_metrics");
70  config->enabled = 1;
71  config->core_metrics_enabled = 0;
72 
73  return config;
74 }
75 
76 static CURL *get_curl_instance(void)
77 {
78  CURL *curl;
79 
80  curl = curl_easy_init();
81  if (!curl) {
82  return NULL;
83  }
84 
85  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
86  curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180);
87  curl_easy_setopt(curl, CURLOPT_USERAGENT, GLOBAL_USERAGENT);
88  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
89  curl_easy_setopt(curl, CURLOPT_URL, server_uri);
90 
91  return curl;
92 }
93 
94 static size_t curl_write_string_callback(void *contents, size_t size, size_t nmemb, void *userdata)
95 {
96  struct ast_str **buffer = userdata;
97  size_t realsize = size * nmemb;
98  char *rawdata;
99 
100  rawdata = ast_malloc(realsize + 1);
101  if (!rawdata) {
102  return 0;
103  }
104  memcpy(rawdata, contents, realsize);
105  rawdata[realsize] = 0;
106  ast_str_append(buffer, 0, "%s", rawdata);
107  ast_free(rawdata);
108 
109  return realsize;
110 }
111 
113 {
114  strcpy(metric->value, "2");
115 }
116 
117 AST_TEST_DEFINE(metric_values)
118 {
119  RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
120  RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
121  int res;
124  "test_counter_one",
125  "A test counter",
126  NULL);
129  "test_counter_two",
130  "A test counter",
133 
134  switch (cmd) {
135  case TEST_INIT:
136  info->name = __func__;
137  info->category = CATEGORY;
138  info->summary = "Test value generation/respecting in metrics";
139  info->description =
140  "Metrics have two ways to provide values when the HTTP callback\n"
141  "is invoked:\n"
142  "1. By using the direct value that resides in the metric\n"
143  "2. By providing a callback function to specify the value\n"
144  "This test verifies that both function appropriately when the\n"
145  "HTTP callback is called.";
146  return AST_TEST_NOT_RUN;
147  case TEST_EXECUTE:
148  break;
149  }
150 
151  buffer = ast_str_create(128);
152  if (!buffer) {
153  return AST_TEST_FAIL;
154  }
155 
156  curl = get_curl_instance();
157  if (!curl) {
158  return AST_TEST_FAIL;
159  }
160 
161  ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter_one) == 0, result, metric_values_cleanup);
162  ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter_two) == 0, result, metric_values_cleanup);
163  strcpy(test_counter_one.value, "1");
164 
165  ast_test_status_update(test, " -> CURLing request...\n");
166  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);
167  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
168  res = curl_easy_perform(curl);
169  if (res != CURLE_OK) {
170  ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
171  result = AST_TEST_FAIL;
172  goto metric_values_cleanup;
173  }
174 
175  ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));
176  ast_test_validate_cleanup(test, strstr(ast_str_buffer(buffer),
177  "# HELP test_counter_one A test counter\n"
178  "# TYPE test_counter_one counter\n"
179  "test_counter_one 1\n"
180  "# HELP test_counter_two A test counter\n"
181  "# TYPE test_counter_two counter\n"
182  "test_counter_two 2\n") != NULL, result, metric_values_cleanup);
183 
184 metric_values_cleanup:
185  prometheus_metric_unregister(&test_counter_one);
186  prometheus_metric_unregister(&test_counter_two);
187 
188  return result;
189 }
190 
191 static void prometheus_metric_callback(struct ast_str **output)
192 {
195  "test_counter",
196  "A test counter",
197  NULL);
198 
199  prometheus_metric_to_string(&test_counter, output);
200 }
201 
202 AST_TEST_DEFINE(metric_callback_register)
203 {
204  RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
205  RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
206  int res;
207  struct prometheus_callback callback = {
208  .name = "test_callback",
209  .callback_fn = &prometheus_metric_callback,
210  };
211 
212  switch (cmd) {
213  case TEST_INIT:
214  info->name = __func__;
215  info->category = CATEGORY;
216  info->summary = "Test registration of callbacks";
217  info->description =
218  "This test covers callback registration. It registers\n"
219  "a callback that is invoked when an HTTP request is made,\n"
220  "and it verifies that during said callback the output to\n"
221  "the response string is correctly appended to. It also verifies\n"
222  "that unregistered callbacks are not invoked.";
223  return AST_TEST_NOT_RUN;
224  case TEST_EXECUTE:
225  break;
226  }
227 
228  buffer = ast_str_create(128);
229  if (!buffer) {
230  return AST_TEST_FAIL;
231  }
232 
233  ast_test_validate(test, prometheus_callback_register(&callback) == 0);
234 
235  curl = get_curl_instance();
236  if (!curl) {
237  return AST_TEST_NOT_RUN;
238  }
239 
240  ast_test_status_update(test, " -> CURLing request...\n");
241  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);
242  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
243  res = curl_easy_perform(curl);
244  if (res != CURLE_OK) {
245  ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
246  return AST_TEST_FAIL;
247  }
248 
249  ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));
250  ast_test_validate(test, strstr(ast_str_buffer(buffer),
251  "# HELP test_counter A test counter\n"
252  "# TYPE test_counter counter\n"
253  "test_counter 0\n") != NULL);
254 
256 
257  return AST_TEST_PASS;
258 }
259 
260 AST_TEST_DEFINE(metric_register)
261 {
264  "test_counter",
265  "A test counter",
266  NULL);
268  RAII_VAR(struct prometheus_metric *, test_gauge_child_one, NULL, prometheus_metric_free_wrapper);
269  RAII_VAR(struct prometheus_metric *, test_gauge_child_two, NULL, prometheus_metric_free_wrapper);
272 
273  switch (cmd) {
274  case TEST_INIT:
275  info->name = __func__;
276  info->category = CATEGORY;
277  info->summary = "Test registration of metrics";
278  info->description =
279  "This test covers the following registration scenarios:\n"
280  "- Nominal registration of simple metrics\n"
281  "- Registration of metrics with different allocation strategies\n"
282  "- Nested metrics with label families\n"
283  "- Off nominal registration with simple name collisions\n"
284  "- Off nominal registration with label collisions";
285  return AST_TEST_NOT_RUN;
286  case TEST_EXECUTE:
287  break;
288  }
289 
290  ast_test_status_update(test, "Testing nominal registration\n");
291  ast_test_status_update(test, "-> Static metric\n");
292  ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter) == 0, result, metric_register_cleanup);
293  ast_test_status_update(test, "-> Malloc'd metric\n");
294  test_gauge = prometheus_gauge_create("test_gauge", "A test gauge");
295  ast_test_validate(test, test_gauge != NULL);
296  ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge) == 0, result, metric_register_cleanup);
297  ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);
298 
299  ast_test_status_update(test, "Testing nominal registration of child metrics\n");
300  test_gauge_child_one = prometheus_gauge_create("test_gauge", "A test gauge");
301  ast_test_validate_cleanup(test, test_gauge_child_one != NULL, result, metric_register_cleanup);
302  PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_one, 0, "key_one", "value_one");
303  PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_one, 1, "key_two", "value_one");
304  test_gauge_child_two = prometheus_gauge_create("test_gauge", "A test gauge");
305  ast_test_validate_cleanup(test, test_gauge_child_two != NULL, result, metric_register_cleanup);
306  PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_two, 0, "key_one", "value_two");
307  PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_two, 1, "key_two", "value_two");
308  ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge_child_one) == 0, result, metric_register_cleanup);
309  ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge_child_two) == 0, result, metric_register_cleanup);
310  ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);
311  ast_test_validate_cleanup(test, test_gauge->children.first == test_gauge_child_one, result, metric_register_cleanup);
312  ast_test_validate_cleanup(test, test_gauge->children.last == test_gauge_child_two, result, metric_register_cleanup);
313 
314  ast_test_status_update(test, "Testing name collisions\n");
315  bad_metric = prometheus_counter_create("test_counter", "A test counter");
316  ast_test_validate_cleanup(test, bad_metric != NULL, result, metric_register_cleanup);
317  ast_test_validate_cleanup(test, prometheus_metric_register(bad_metric) != 0, result, metric_register_cleanup);
318  prometheus_metric_free(bad_metric);
319  bad_metric = NULL;
320 
321  ast_test_status_update(test, "Testing label collisions\n");
322  bad_metric = prometheus_gauge_create("test_gauge", "A test gauge");
323  ast_test_validate_cleanup(test, bad_metric != NULL, result, metric_register_cleanup);
324  PROMETHEUS_METRIC_SET_LABEL(bad_metric, 0, "key_one", "value_one");
325  PROMETHEUS_METRIC_SET_LABEL(bad_metric, 1, "key_two", "value_one");
326  ast_test_validate_cleanup(test, prometheus_metric_register(bad_metric) != 0, result, metric_register_cleanup);
327  prometheus_metric_free(bad_metric);
328  bad_metric = NULL;
329 
330  ast_test_status_update(test, "Testing removal of metrics\n");
331  prometheus_metric_unregister(test_gauge_child_two);
332  test_gauge_child_two = NULL;
333 
334  ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);
335  prometheus_metric_unregister(test_gauge);
336  test_gauge = NULL;
337 
338  ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);
339  prometheus_metric_unregister(test_gauge_child_one);
340  test_gauge_child_one = NULL;
341 
342  ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 1, result, metric_register_cleanup);
343  prometheus_metric_unregister(&test_counter);
344 
345  ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 0, result, metric_register_cleanup);
346 
347  return AST_TEST_PASS;
348 
349 metric_register_cleanup:
350  prometheus_metric_unregister(&test_counter);
351  return result;
352 }
353 
354 AST_TEST_DEFINE(counter_to_string)
355 {
358  "test_counter",
359  "A test counter",
360  NULL);
361  struct prometheus_metric test_counter_child_one = PROMETHEUS_METRIC_STATIC_INITIALIZATION(
363  "test_counter",
364  "A test counter",
365  NULL);
366  struct prometheus_metric test_counter_child_two = PROMETHEUS_METRIC_STATIC_INITIALIZATION(
368  "test_counter",
369  "A test counter",
370  NULL);
371  RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
372 
373  switch (cmd) {
374  case TEST_INIT:
375  info->name = __func__;
376  info->category = CATEGORY;
377  info->summary = "Test formatting of counters";
378  info->description =
379  "This test covers the formatting of printed counters";
380  return AST_TEST_NOT_RUN;
381  case TEST_EXECUTE:
382  break;
383  }
384 
385  buffer = ast_str_create(128);
386  if (!buffer) {
387  return AST_TEST_FAIL;
388  }
389 
390  PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_one, 0, "key_one", "value_one");
391  PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_one, 1, "key_two", "value_one");
392  PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_two, 0, "key_one", "value_two");
393  PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_two, 1, "key_two", "value_two");
394  AST_LIST_INSERT_TAIL(&test_counter.children, &test_counter_child_one, entry);
395  AST_LIST_INSERT_TAIL(&test_counter.children, &test_counter_child_two, entry);
396  prometheus_metric_to_string(&test_counter, &buffer);
397  ast_test_validate(test, strcmp(ast_str_buffer(buffer),
398  "# HELP test_counter A test counter\n"
399  "# TYPE test_counter counter\n"
400  "test_counter 0\n"
401  "test_counter{key_one=\"value_one\",key_two=\"value_one\"} 0\n"
402  "test_counter{key_one=\"value_two\",key_two=\"value_two\"} 0\n") == 0);
403 
404  return AST_TEST_PASS;
405 }
406 
407 AST_TEST_DEFINE(counter_create)
408 {
410 
411  switch (cmd) {
412  case TEST_INIT:
413  info->name = __func__;
414  info->category = CATEGORY;
415  info->summary = "Test creation (and destruction) of malloc'd counters";
416  info->description =
417  "This test covers creating a counter metric and destroying\n"
418  "it. The metric should be malloc'd.";
419  return AST_TEST_NOT_RUN;
420  case TEST_EXECUTE:
421  break;
422  }
423 
424  metric = prometheus_counter_create("test_counter", "A test counter");
425  ast_test_validate(test, metric != NULL);
426  ast_test_validate(test, metric->type == PROMETHEUS_METRIC_COUNTER);
427  ast_test_validate(test, metric->allocation_strategy = PROMETHEUS_METRIC_MALLOCD);
428  ast_test_validate(test, !strcmp(metric->help, "A test counter"));
429  ast_test_validate(test, !strcmp(metric->name, "test_counter"));
430  ast_test_validate(test, !strcmp(metric->value, ""));
431  ast_test_validate(test, metric->children.first == NULL);
432  ast_test_validate(test, metric->children.last == NULL);
433 
434  return AST_TEST_PASS;
435 }
436 
437 AST_TEST_DEFINE(gauge_to_string)
438 {
441  "test_gauge",
442  "A test gauge",
443  NULL);
444  struct prometheus_metric test_gauge_child_one = PROMETHEUS_METRIC_STATIC_INITIALIZATION(
446  "test_gauge",
447  "A test gauge",
448  NULL);
449  struct prometheus_metric test_gauge_child_two = PROMETHEUS_METRIC_STATIC_INITIALIZATION(
451  "test_gauge",
452  "A test gauge",
453  NULL);
454  RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
455 
456  switch (cmd) {
457  case TEST_INIT:
458  info->name = __func__;
459  info->category = CATEGORY;
460  info->summary = "Test formatting of gauges";
461  info->description =
462  "This test covers the formatting of printed gauges";
463  return AST_TEST_NOT_RUN;
464  case TEST_EXECUTE:
465  break;
466  }
467 
468  buffer = ast_str_create(128);
469  if (!buffer) {
470  return AST_TEST_FAIL;
471  }
472 
473  PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_one, 0, "key_one", "value_one");
474  PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_one, 1, "key_two", "value_one");
475  PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_two, 0, "key_one", "value_two");
476  PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_two, 1, "key_two", "value_two");
477  AST_LIST_INSERT_TAIL(&test_gauge.children, &test_gauge_child_one, entry);
478  AST_LIST_INSERT_TAIL(&test_gauge.children, &test_gauge_child_two, entry);
479  prometheus_metric_to_string(&test_gauge, &buffer);
480  ast_test_validate(test, strcmp(ast_str_buffer(buffer),
481  "# HELP test_gauge A test gauge\n"
482  "# TYPE test_gauge gauge\n"
483  "test_gauge 0\n"
484  "test_gauge{key_one=\"value_one\",key_two=\"value_one\"} 0\n"
485  "test_gauge{key_one=\"value_two\",key_two=\"value_two\"} 0\n") == 0);
486 
487  return AST_TEST_PASS;
488 }
489 
490 AST_TEST_DEFINE(gauge_create)
491 {
493 
494  switch (cmd) {
495  case TEST_INIT:
496  info->name = __func__;
497  info->category = CATEGORY;
498  info->summary = "Test creation (and destruction) of malloc'd gauges";
499  info->description =
500  "This test covers creating a gauge metric and destroying\n"
501  "it. The metric should be malloc'd.";
502  return AST_TEST_NOT_RUN;
503  case TEST_EXECUTE:
504  break;
505  }
506 
507  metric = prometheus_gauge_create("test_gauge", "A test gauge");
508  ast_test_validate(test, metric != NULL);
509  ast_test_validate(test, metric->type == PROMETHEUS_METRIC_GAUGE);
510  ast_test_validate(test, metric->allocation_strategy = PROMETHEUS_METRIC_MALLOCD);
511  ast_test_validate(test, !strcmp(metric->help, "A test gauge"));
512  ast_test_validate(test, !strcmp(metric->name, "test_gauge"));
513  ast_test_validate(test, !strcmp(metric->value, ""));
514  ast_test_validate(test, metric->children.first == NULL);
515  ast_test_validate(test, metric->children.last == NULL);
516 
517  return AST_TEST_PASS;
518 }
519 
520 AST_TEST_DEFINE(config_general_basic_auth)
521 {
522  RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
524  int res;
525  long response_code;
526 
527  switch (cmd) {
528  case TEST_INIT:
529  info->name = __func__;
530  info->category = CATEGORY;
531  info->summary = "Test basic auth handling";
532  info->description =
533  "This test covers authentication of requests";
534  return AST_TEST_NOT_RUN;
535  case TEST_EXECUTE:
536  break;
537  }
538 
539  config = config_alloc();
540  if (!config) {
541  return AST_TEST_NOT_RUN;
542  }
543  ast_string_field_set(config, auth_username, "foo");
544  ast_string_field_set(config, auth_password, "bar");
545  /* Prometheus module owns the ref after this call */
547  ao2_ref(config, -1);
548 
549  curl = get_curl_instance();
550  if (!curl) {
551  return AST_TEST_NOT_RUN;
552  }
553 
554  ast_test_status_update(test, "Testing without auth credentials\n");
555  ast_test_status_update(test, " -> CURLing request...\n");
556  res = curl_easy_perform(curl);
557  if (res != CURLE_OK) {
558  ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
559  return AST_TEST_FAIL;
560  }
561  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
562  ast_test_status_update(test, " -> CURL returned %ld\n", response_code);
563  ast_test_validate(test, response_code == 401);
564 
565  ast_test_status_update(test, "Testing with invalid auth credentials\n");
566  ast_test_status_update(test, " -> CURLing request...\n");
567  curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
568  curl_easy_setopt(curl, CURLOPT_USERPWD, "matt:jordan");
569  res = curl_easy_perform(curl);
570  if (res != CURLE_OK) {
571  ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
572  return AST_TEST_FAIL;
573  }
574  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
575  ast_test_status_update(test, " -> CURL returned %ld\n", response_code);
576  ast_test_validate(test, response_code == 401);
577 
578  ast_test_status_update(test, "Testing with valid auth credentials\n");
579  ast_test_status_update(test, " -> CURLing request...\n");
580  curl_easy_setopt(curl, CURLOPT_USERPWD, "foo:bar");
581  res = curl_easy_perform(curl);
582  if (res != CURLE_OK) {
583  ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
584  return AST_TEST_FAIL;
585  }
586  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
587  ast_test_status_update(test, " -> CURL returned %ld\n", response_code);
588  ast_test_validate(test, response_code == 200);
589 
590  return AST_TEST_PASS;
591 }
592 
593 AST_TEST_DEFINE(config_general_enabled)
594 {
595  RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
597  int res;
598  long response_code;
599 
600  switch (cmd) {
601  case TEST_INIT:
602  info->name = __func__;
603  info->category = CATEGORY;
604  info->summary = "Test handling of enable/disable";
605  info->description =
606  "When disabled, the module should return a 503.\n"
607  "This test verifies that it actually occurs.";
608  return AST_TEST_NOT_RUN;
609  case TEST_EXECUTE:
610  break;
611  }
612 
613  config = config_alloc();
614  if (!config) {
615  return AST_TEST_NOT_RUN;
616  }
617  config->enabled = 0;
618  /* Prometheus module owns the ref after this call */
620  ao2_ref(config, -1);
621 
622  curl = get_curl_instance();
623  if (!curl) {
624  return AST_TEST_NOT_RUN;
625  }
626 
627  ast_test_status_update(test, " -> CURLing request...\n");
628  res = curl_easy_perform(curl);
629  if (res != CURLE_OK) {
630  ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
631  return AST_TEST_FAIL;
632  }
633  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
634  ast_test_status_update(test, " -> CURL returned %ld\n", response_code);
635  ast_test_validate(test, response_code == 503);
636 
637  return AST_TEST_PASS;
638 }
639 
640 AST_TEST_DEFINE(config_general_core_metrics)
641 {
642  RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);
643  RAII_VAR(struct ast_str *, buffer, NULL, ast_free);
645  int res;
646 
647  switch (cmd) {
648  case TEST_INIT:
649  info->name = __func__;
650  info->category = CATEGORY;
651  info->summary = "Test producing core metrics";
652  info->description =
653  "This test covers the core metrics that are produced\n"
654  "by the basic Prometheus module.";
655  return AST_TEST_NOT_RUN;
656  case TEST_EXECUTE:
657  break;
658  }
659 
660  buffer = ast_str_create(128);
661  if (!buffer) {
662  return AST_TEST_NOT_RUN;
663  }
664 
665  config = config_alloc();
666  if (!config) {
667  return AST_TEST_NOT_RUN;
668  }
669  config->core_metrics_enabled = 1;
670  /* Prometheus module owns the ref after this call */
672  ao2_ref(config, -1);
673 
674  curl = get_curl_instance();
675  if (!curl) {
676  return AST_TEST_NOT_RUN;
677  }
678 
679  ast_test_status_update(test, " -> CURLing request...\n");
680  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);
681  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
682  res = curl_easy_perform(curl);
683  if (res != CURLE_OK) {
684  ast_test_status_update(test, "Failed to execute CURL: %d\n", res);
685  return AST_TEST_FAIL;
686  }
687  ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));
688 
689  ast_test_status_update(test, " -> Checking for core properties\n");
690  ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_properties") != NULL);
691 
692  ast_test_status_update(test, " -> Checking for uptime\n");
693  ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_uptime_seconds") != NULL);
694 
695  ast_test_status_update(test, " -> Checking for last reload\n");
696  ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_last_reload_seconds") != NULL);
697 
698  ast_test_status_update(test, " -> Checking for scrape time\n");
699  ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_scrape_time_ms") != NULL);
700 
701  return AST_TEST_PASS;
702 }
703 
704 static int process_config(int reload)
705 {
706  struct ast_config *config;
707  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
708  const char *bindaddr;
709  const char *bindport;
710  const char *prefix;
711  const char *enabled;
712 
713  config = ast_config_load("http.conf", config_flags);
714  if (!config || config == CONFIG_STATUS_FILEINVALID) {
715  ast_log(AST_LOG_NOTICE, "HTTP config file is invalid; declining load");
716  return -1;
717  } else if (config == CONFIG_STATUS_FILEUNCHANGED) {
718  return 0;
719  }
720 
721  enabled = ast_config_option(config, "general", "enabled");
722  if (!enabled || ast_false(enabled)) {
723  ast_config_destroy(config);
724  ast_log(AST_LOG_NOTICE, "HTTP server is disabled; declining load");
725  return -1;
726  }
727 
728  /* Construct our Server URI */
729  bindaddr = ast_config_option(config, "general", "bindaddr");
730  if (!bindaddr) {
731  ast_config_destroy(config);
732  ast_log(AST_LOG_NOTICE, "HTTP config file fails to specify 'bindaddr'; declining load");
733  return -1;
734  }
735 
736  bindport = ast_config_option(config, "general", "bindport");
737  if (!bindport) {
738  bindport = "8088";
739  }
740 
741  prefix = ast_config_option(config, "general", "prefix");
742 
743  snprintf(server_uri, sizeof(server_uri), "http://%s:%s%s/test_metrics", bindaddr, bindport, S_OR(prefix, ""));
744 
745  ast_config_destroy(config);
746 
747  return 0;
748 }
749 
750 static int test_init_cb(struct ast_test_info *info, struct ast_test *test)
751 {
752  struct prometheus_general_config *new_module_config;
753 
754  new_module_config = config_alloc();
755  if (!new_module_config) {
756  return -1;
757  }
758 
759  module_config = prometheus_general_config_get();
760  prometheus_general_config_set(new_module_config);
761 
762  /* Allow the module to own the ref */
763  ao2_ref(new_module_config, -1);
764 
765  return 0;
766 }
767 
768 static int test_cleanup_cb(struct ast_test_info *info, struct ast_test *test)
769 {
770  prometheus_general_config_set(module_config);
771  ao2_cleanup(module_config);
772 
773  return 0;
774 }
775 
776 static int reload_module(void)
777 {
778  return process_config(1);
779 }
780 
781 static int unload_module(void)
782 {
783  AST_TEST_UNREGISTER(metric_values);
784  AST_TEST_UNREGISTER(metric_callback_register);
785  AST_TEST_UNREGISTER(metric_register);
786 
787  AST_TEST_UNREGISTER(counter_to_string);
788  AST_TEST_UNREGISTER(counter_create);
789  AST_TEST_UNREGISTER(gauge_to_string);
790  AST_TEST_UNREGISTER(gauge_create);
791 
792  AST_TEST_UNREGISTER(config_general_enabled);
793  AST_TEST_UNREGISTER(config_general_basic_auth);
794  AST_TEST_UNREGISTER(config_general_core_metrics);
795 
796  return 0;
797 }
798 
799 static int load_module(void)
800 {
801  if (process_config(0)) {
803  }
804 
805  AST_TEST_REGISTER(metric_values);
806  AST_TEST_REGISTER(metric_callback_register);
807  AST_TEST_REGISTER(metric_register);
808 
809  AST_TEST_REGISTER(counter_to_string);
810  AST_TEST_REGISTER(counter_create);
811  AST_TEST_REGISTER(gauge_to_string);
812  AST_TEST_REGISTER(gauge_create);
813 
814  AST_TEST_REGISTER(config_general_enabled);
815  AST_TEST_REGISTER(config_general_basic_auth);
816  AST_TEST_REGISTER(config_general_core_metrics);
817 
820 
822 }
823 
824 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Prometheus Core Unit Tests",
825  .load = load_module,
827  .unload = unload_module,
828  .requires = "res_prometheus",
829 );
Contains all the initialization information required to store a new test definition.
Definition: test.h:221
#define GLOBAL_USERAGENT
unsigned int core_metrics_enabled
Whether or not core metrics are enabled.
Asterisk main include file. File version handling, generic pbx functions.
An actual, honest to god, metric.
int prometheus_metric_register(struct prometheus_metric *metric)
char * config
Definition: conf2ael.c:66
Prometheus general configuration.
int ast_test_register_init(const char *category, ast_test_init_cb_t *cb)
Definition: test.c:160
int prometheus_callback_register(struct prometheus_callback *callback)
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define CONFIG_STATUS_FILEINVALID
A metric whose value always goes up.
const char * name
The name of our callback (always useful for debugging)
Test Framework API.
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
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:1091
static struct prometheus_general_config * config_alloc(void)
static int reload_module(void)
int prometheus_metric_unregister(struct prometheus_metric *metric)
Remove a registered metric.
#define NULL
Definition: resample.c:96
static char server_uri[512]
void prometheus_metric_free(struct prometheus_metric *metric)
Destroy a metric and all its children.
#define PROMETHEUS_METRIC_STATIC_INITIALIZATION(mtype, n, h, cb)
Convenience macro for initializing a metric on the stack.
void prometheus_general_config_set(struct prometheus_general_config *config)
Set the configuration for the module.
void prometheus_metric_to_string(struct prometheus_metric *metric, struct ast_str **output)
Convert a metric (and its children) into Prometheus compatible text.
#define AST_LOG_NOTICE
Definition: logger.h:268
Configuration File Parser.
#define ast_log
Definition: astobj2.c:42
#define ast_config_load(filename, flags)
Load a config file.
#define PROMETHEUS_METRIC_SET_LABEL(metric, label, n, v)
Convenience macro for setting a label / value in a metric.
static void metric_values_get_counter_value_cb(struct prometheus_metric *metric)
#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:911
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
static int unload_module(void)
static int load_module(void)
const ast_string_field auth_password
Auth password for Basic Auth.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
const char * contents
struct prometheus_metric * prometheus_gauge_create(const char *name, const char *help)
Create a malloc&#39;d gauge metric.
int ast_test_register_cleanup(const char *category, ast_test_cleanup_cb_t *cb)
Definition: test.c:177
#define CONFIG_STATUS_FILEUNCHANGED
struct prometheus_metric::@312 children
A list of children metrics.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static void prometheus_metric_free_wrapper(void *ptr)
static CURL * get_curl_instance(void)
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
def info(msg)
static size_t curl_write_string_callback(void *contents, size_t size, size_t nmemb, void *userdata)
char value[PROMETHEUS_MAX_VALUE_LENGTH]
The current value.
void * prometheus_general_config_alloc(void)
Allocate a new configuration object.
#define ast_free(a)
Definition: astmm.h:182
static int reload(void)
Definition: cdr_mysql.c:741
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define CATEGORY
void prometheus_callback_unregister(struct prometheus_callback *callback)
Remove a registered callback.
struct prometheus_general_config * module_config
Structure used to handle boolean flags.
Definition: utils.h:199
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
const ast_string_field uri
The HTTP URI we register ourselves to.
Definition: test.c:65
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
struct prometheus_metric * prometheus_counter_create(const char *name, const char *help)
Create a malloc&#39;d counter metric.
static void curl_free_wrapper(void *ptr)
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: main/utils.c:1968
int prometheus_metric_registered_count(void)
static PGresult * result
Definition: cel_pgsql.c:88
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:684
Definition: search.h:40
The metric was allocated on the heap.
static int process_config(int reload)
struct prometheus_general_config * prometheus_general_config_get(void)
Retrieve the current configuration of the module.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static void prometheus_metric_callback(struct ast_str **output)
static int test_init_cb(struct ast_test_info *info, struct ast_test *test)
Defines a callback that will be invoked when the HTTP route is called.
static int test_cleanup_cb(struct ast_test_info *info, struct ast_test *test)
ast_test_result_state
Definition: test.h:200
struct ast_sockaddr bindaddr
Definition: chan_ooh323.c:353
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
const ast_string_field auth_username
Auth username for Basic Auth.
static char prefix[MAX_PREFIX]
Definition: http.c:141
unsigned int enabled
Whether or not the module is enabled.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
static int enabled
Definition: dnsmgr.c:91
AST_TEST_DEFINE(metric_values)